From cef2344e70b4ab90cbbb01aac00f248976d273df Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Sat, 5 Sep 2009 15:14:46 -0700 Subject: [PATCH] Rework EAS account creation & ssl operation * Fixes 2048663, 2025029, and 2100131 * Add "Trust Certificates" checkbox in EAS account creation * Use custom ClientConnectionManager for HttpClient with registry for plain, ssl, and tssl (trusted ssl) connection * Use a ConnectionPool for HttpClient connections * Remove "Domain" checkbox in EAS account creation * Remove tests related to the "Domain" field * TODO Write a test for valid usernames (requires a bit of research) , , /\ are all valid, but there might be others Change-Id: I4a0338df5960bfd3d679a88aaf22d1c49f49992b --- res/layout/account_setup_exchange.xml | 18 ++--- res/values/strings.xml | 2 + .../activity/setup/AccountSetupExchange.java | 44 ++++++------ .../email/mail/store/ExchangeStore.java | 10 +-- .../email/mail/store/TrustManagerFactory.java | 15 ++-- .../android/email/provider/EmailContent.java | 23 ++++-- .../email/service/EmailServiceProxy.java | 6 +- .../android/exchange/AbstractSyncService.java | 7 +- src/com/android/exchange/EasSyncService.java | 25 +++++-- src/com/android/exchange/IEmailService.aidl | 2 +- src/com/android/exchange/SyncManager.java | 72 ++++++++++++++++++- .../setup/AccountSetupExchangeTests.java | 56 ++------------- 12 files changed, 165 insertions(+), 115 deletions(-) diff --git a/res/layout/account_setup_exchange.xml b/res/layout/account_setup_exchange.xml index 4173bcf00..2dd256625 100644 --- a/res/layout/account_setup_exchange.xml +++ b/res/layout/account_setup_exchange.xml @@ -69,24 +69,16 @@ android:imeOptions="actionDone" android:layout_height="wrap_content" android:layout_width="fill_parent" /> - - + Enter domain here Use secure connection (SSL) + + Accept all SSL certificates Account options diff --git a/src/com/android/email/activity/setup/AccountSetupExchange.java b/src/com/android/email/activity/setup/AccountSetupExchange.java index 8f8f08ae0..7d09ab079 100644 --- a/src/com/android/email/activity/setup/AccountSetupExchange.java +++ b/src/com/android/email/activity/setup/AccountSetupExchange.java @@ -19,18 +19,20 @@ package com.android.email.activity.setup; import com.android.email.R; import com.android.email.Utility; import com.android.email.provider.EmailContent; +import com.android.email.provider.EmailContent.Account; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.Editable; -import android.text.TextUtils; import android.text.TextWatcher; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.EditText; +import android.widget.CompoundButton.OnCheckedChangeListener; import java.net.URI; import java.net.URISyntaxException; @@ -45,7 +47,8 @@ import java.net.URISyntaxException; * User (login) * Password */ -public class AccountSetupExchange extends Activity implements OnClickListener { +public class AccountSetupExchange extends Activity implements OnClickListener, + OnCheckedChangeListener { private static final String EXTRA_ACCOUNT = "account"; private static final String EXTRA_MAKE_DEFAULT = "makeDefault"; private static final String EXTRA_EAS_FLOW = "easFlow"; @@ -53,14 +56,14 @@ public class AccountSetupExchange extends Activity implements OnClickListener { private EditText mUsernameView; private EditText mPasswordView; private EditText mServerView; - private EditText mDomainView; private CheckBox mSslSecurityView; + private CheckBox mTrustCertificatesView; private Button mNextButton; - private EmailContent.Account mAccount; + private Account mAccount; private boolean mMakeDefault; - public static void actionIncomingSettings(Activity fromActivity, EmailContent.Account account, + public static void actionIncomingSettings(Activity fromActivity, Account account, boolean makeDefault, boolean easFlowMode) { Intent i = new Intent(fromActivity, AccountSetupExchange.class); i.putExtra(EXTRA_ACCOUNT, account); @@ -69,7 +72,7 @@ public class AccountSetupExchange extends Activity implements OnClickListener { fromActivity.startActivity(i); } - public static void actionEditIncomingSettings(Activity fromActivity, EmailContent.Account account) + public static void actionEditIncomingSettings(Activity fromActivity, Account account) { Intent i = new Intent(fromActivity, AccountSetupExchange.class); i.setAction(Intent.ACTION_EDIT); @@ -81,7 +84,7 @@ public class AccountSetupExchange extends Activity implements OnClickListener { * For now, we'll simply replicate outgoing, for the purpose of satisfying the * account settings flow. */ - public static void actionEditOutgoingSettings(Activity fromActivity, EmailContent.Account account) + public static void actionEditOutgoingSettings(Activity fromActivity, Account account) { Intent i = new Intent(fromActivity, AccountSetupExchange.class); i.setAction(Intent.ACTION_EDIT); @@ -97,8 +100,9 @@ public class AccountSetupExchange extends Activity implements OnClickListener { mUsernameView = (EditText) findViewById(R.id.account_username); mPasswordView = (EditText) findViewById(R.id.account_password); mServerView = (EditText) findViewById(R.id.account_server); - mDomainView = (EditText) findViewById(R.id.account_domain); mSslSecurityView = (CheckBox) findViewById(R.id.account_ssl); + mSslSecurityView.setOnCheckedChangeListener(this); + mTrustCertificatesView = (CheckBox) findViewById(R.id.account_trust_certificates); mNextButton = (Button)findViewById(R.id.next); mNextButton.setOnClickListener(this); @@ -121,7 +125,6 @@ public class AccountSetupExchange extends Activity implements OnClickListener { mUsernameView.addTextChangedListener(validationTextWatcher); mPasswordView.addTextChangedListener(validationTextWatcher); mServerView.addTextChangedListener(validationTextWatcher); - mDomainView.addTextChangedListener(validationTextWatcher); mAccount = (EmailContent.Account) getIntent().getParcelableExtra(EXTRA_ACCOUNT); mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false); @@ -164,12 +167,10 @@ public class AccountSetupExchange extends Activity implements OnClickListener { mServerView.setText(uri.getHost()); } - String domain = uri.getPath(); - if (!TextUtils.isEmpty(domain)) { - mDomainView.setText(domain.substring(1)); - } - - mSslSecurityView.setChecked(uri.getScheme().contains("ssl")); + boolean ssl = uri.getScheme().contains("ssl"); + mSslSecurityView.setChecked(ssl); + mTrustCertificatesView.setChecked(uri.getScheme().contains("tssl")); + mTrustCertificatesView.setVisibility(ssl ? View.VISIBLE : View.GONE); } catch (URISyntaxException use) { /* @@ -233,15 +234,12 @@ public class AccountSetupExchange extends Activity implements OnClickListener { */ private URI getUri() throws URISyntaxException { boolean sslRequired = mSslSecurityView.isChecked(); - String scheme = sslRequired ? "eas+ssl+" : "eas"; + boolean trustCertificates = mTrustCertificatesView.isChecked(); + String scheme = (sslRequired) ? (trustCertificates ? "eas+tssl+" : "eas+ssl+") : "eas"; String userInfo = mUsernameView.getText().toString().trim() + ":" + mPasswordView.getText().toString().trim(); String host = mServerView.getText().toString().trim(); - String domain = mDomainView.getText().toString().trim(); String path = null; - if (domain.length() > 0) { - path = "/" + domain; - } URI uri = new URI( scheme, @@ -281,4 +279,10 @@ public class AccountSetupExchange extends Activity implements OnClickListener { break; } } + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (buttonView.getId() == R.id.account_ssl) { + mTrustCertificatesView.setVisibility(isChecked ? View.VISIBLE : View.GONE); + } + } } diff --git a/src/com/android/email/mail/store/ExchangeStore.java b/src/com/android/email/mail/store/ExchangeStore.java index aef421c94..c5d81745f 100644 --- a/src/com/android/email/mail/store/ExchangeStore.java +++ b/src/com/android/email/mail/store/ExchangeStore.java @@ -46,7 +46,6 @@ import java.util.HashMap; public class ExchangeStore extends Store { public static final String LOG_TAG = "ExchangeStore"; - private Context mContext; private URI mUri; private final ExchangeTransport mTransport; @@ -66,7 +65,6 @@ public class ExchangeStore extends Store { */ private ExchangeStore(String _uri, Context context, PersistentDataCallbacks callbacks) throws MessagingException { - mContext = context; try { mUri = new URI(_uri); } catch (URISyntaxException e) { @@ -157,14 +155,14 @@ public class ExchangeStore extends Store { private static HashMap sUriToInstanceMap = new HashMap(); - private static final HashMap sFolderMap = new HashMap(); /** * Public factory. The transport should be a singleton (per Uri) */ public synchronized static ExchangeTransport getInstance(URI uri, Context context) throws MessagingException { - if (!uri.getScheme().equals("eas") && !uri.getScheme().equals("eas+ssl+")) { + if (!uri.getScheme().equals("eas") && !uri.getScheme().equals("eas+ssl+") && + !uri.getScheme().equals("eas+tssl+")) { throw new MessagingException("Invalid scheme"); } @@ -221,9 +219,11 @@ public class ExchangeStore extends Store { public void checkSettings(URI uri) throws MessagingException { setUri(uri); boolean ssl = uri.getScheme().contains("ssl+"); + boolean tssl = uri.getScheme().contains("tssl+"); try { + int port = ssl ? 443 : 80; int result = new EmailServiceProxy(mContext, SyncManager.class) - .validate("eas", mHost, mUsername, mPassword, ssl ? 443 : 80, ssl); + .validate("eas", mHost, mUsername, mPassword, port, ssl, tssl); if (result != MessagingException.NO_ERROR) { if (result == MessagingException.AUTHENTICATION_FAILED) { throw new AuthenticationFailedException("Authentication failed."); diff --git a/src/com/android/email/mail/store/TrustManagerFactory.java b/src/com/android/email/mail/store/TrustManagerFactory.java index 91154bc3a..bcd6a5593 100644 --- a/src/com/android/email/mail/store/TrustManagerFactory.java +++ b/src/com/android/email/mail/store/TrustManagerFactory.java @@ -16,18 +16,17 @@ package com.android.email.mail.store; +import org.apache.harmony.xnet.provider.jsse.SSLParameters; + import android.net.http.DomainNameChecker; -import java.security.cert.X509Certificate; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; -import org.apache.harmony.xnet.provider.jsse.SSLParameters; - public final class TrustManagerFactory { - private static X509TrustManager sUnsecureTrustManager - = new SimpleX509TrustManager(); + private static X509TrustManager sUnsecureTrustManager = new SimpleX509TrustManager(); private static class SimpleX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) @@ -63,8 +62,7 @@ public final class TrustManagerFactory { mTrustManager.checkServerTrusted(chain, authType); if (!DomainNameChecker.match(chain[0], mHost)) { - throw new CertificateException("Certificate domain name does not match " - + mHost); + throw new CertificateException("Certificate domain name does not match " + mHost); } } @@ -77,8 +75,7 @@ public final class TrustManagerFactory { } public static X509TrustManager get(String host, boolean secure) { - return secure ? new SecureX509TrustManager( - SSLParameters.getDefaultTrustManager(), host) + return secure ? new SecureX509TrustManager(SSLParameters.getDefaultTrustManager(), host) : sUnsecureTrustManager; } } diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java index 3abd9d1d8..8db282a70 100644 --- a/src/com/android/email/provider/EmailContent.java +++ b/src/com/android/email/provider/EmailContent.java @@ -1916,6 +1916,7 @@ public abstract class EmailContent { public static final int FLAG_SSL = 1; public static final int FLAG_TLS = 2; public static final int FLAG_AUTHENTICATE = 4; + public static final int FLAG_TRUST_ALL_CERTIFICATES = 8; public String mProtocol; public String mAddress; @@ -2013,7 +2014,9 @@ public abstract class EmailContent { */ public String getStoreUri() { String security = ""; - if ((mFlags & FLAG_SSL) != 0) { + if ((mFlags & FLAG_TRUST_ALL_CERTIFICATES) != 0) { + security = "+tssl+"; + } else if ((mFlags & FLAG_SSL) != 0) { security = "+ssl+"; } else if ((mFlags & FLAG_TLS) != 0) { security = "+tls+"; @@ -2068,10 +2071,15 @@ public abstract class EmailContent { mProtocol = (schemeParts.length >= 1) ? schemeParts[0] : null; boolean ssl = false; boolean tls = false; + boolean tssl = false; if (schemeParts.length >= 2) { - if ("ssl".equals(schemeParts[1])) { + String part1 = schemeParts[1]; + if ("tssl".equals(part1)) { ssl = true; - } else if ("tls".equals(schemeParts[1])) { + tssl = true; + } else if ("ssl".equals(part1)) { + ssl = true; + } else if ("tls".equals(part1)) { tls = true; } } @@ -2083,6 +2091,9 @@ public abstract class EmailContent { if (tls) { mFlags |= FLAG_TLS; } + if (tssl) { + mFlags |= FLAG_TRUST_ALL_CERTIFICATES; + } mAddress = uri.getHost(); mPort = uri.getPort(); @@ -2092,11 +2103,11 @@ public abstract class EmailContent { if ("pop3".equals(mProtocol)) { mPort = ssl ? 995 : 110; } else if ("imap".equals(mProtocol)) { - mPort = ssl ? 993 : 143; + mPort = ssl || tssl ? 993 : 143; } else if ("eas".equals(mProtocol)) { - mPort = ssl ? 443 : 80; + mPort = ssl || tssl ? 443 : 80; } else if ("smtp".equals(mProtocol)) { - mPort = ssl ? 465 : 25; + mPort = ssl || tssl ? 465 : 25; } } diff --git a/src/com/android/email/service/EmailServiceProxy.java b/src/com/android/email/service/EmailServiceProxy.java index 1bb0df3c7..423ae695f 100644 --- a/src/com/android/email/service/EmailServiceProxy.java +++ b/src/com/android/email/service/EmailServiceProxy.java @@ -184,12 +184,14 @@ public class EmailServiceProxy implements IEmailService { } public int validate(final String protocol, final String host, final String userName, - final String password, final int port, final boolean ssl) throws RemoteException { + final String password, final int port, final boolean ssl, + final boolean trustCertificates) throws RemoteException { setTask(new Runnable () { public void run() { try { if (mCallback != null) mService.setCallback(mCallback); - mReturn = mService.validate(protocol, host, userName, password, port, ssl); + mReturn = mService.validate(protocol, host, userName, password, port, ssl, + trustCertificates); } catch (RemoteException e) { } } diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java index 15cede872..97cfb8767 100644 --- a/src/com/android/exchange/AbstractSyncService.java +++ b/src/com/android/exchange/AbstractSyncService.java @@ -101,7 +101,7 @@ public abstract class AbstractSyncService implements Runnable { * @throws MessagingException */ public abstract void validateAccount(String host, String userName, String password, int port, - boolean ssl, Context context) throws MessagingException; + boolean ssl, boolean trustCertificates, Context context) throws MessagingException; public AbstractSyncService(Context _context, Mailbox _mailbox) { mContext = _context; @@ -129,12 +129,13 @@ public abstract class AbstractSyncService implements Runnable { * @throws MessagingException */ static public void validate(Class klass, String host, - String userName, String password, int port, boolean ssl, Context context) + String userName, String password, int port, boolean ssl, boolean trustCertificates, + Context context) throws MessagingException { AbstractSyncService svc; try { svc = klass.newInstance(); - svc.validateAccount(host, userName, password, port, ssl, context); + svc.validateAccount(host, userName, password, port, ssl, trustCertificates, context); } catch (IllegalAccessException e) { throw new MessagingException("internal error", e); } catch (InstantiationException e) { diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java index b4f2ac414..7c1e3ae36 100644 --- a/src/com/android/exchange/EasSyncService.java +++ b/src/com/android/exchange/EasSyncService.java @@ -46,6 +46,7 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ClientConnectionManager; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; @@ -65,11 +66,11 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URLEncoder; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.HashMap; public class EasSyncService extends AbstractSyncService { - private static final String EMAIL_WINDOW_SIZE = "5"; public static final String PIM_WINDOW_SIZE = "20"; private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID = @@ -133,6 +134,7 @@ public class EasSyncService extends AbstractSyncService { public String mUserName; public String mPassword; private boolean mSsl = true; + private boolean mTrustSsl = false; public ContentResolver mContentResolver; private String[] mBindArguments = new String[2]; private ArrayList mPingChangeList; @@ -149,6 +151,7 @@ public class EasSyncService extends AbstractSyncService { mContentResolver = _context.getContentResolver(); HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv); mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0; + mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES) != 0; } private EasSyncService(String prefix) { @@ -191,7 +194,7 @@ public class EasSyncService extends AbstractSyncService { @Override public void validateAccount(String hostAddress, String userName, String password, int port, - boolean ssl, Context context) throws MessagingException { + boolean ssl, boolean trustCertificates, Context context) throws MessagingException { try { userLog("Testing EAS: ", hostAddress, ", ", userName, ", ssl = ", ssl ? "1" : "0"); EasSyncService svc = new EasSyncService("%TestAccount%"); @@ -200,6 +203,7 @@ public class EasSyncService extends AbstractSyncService { svc.mUserName = userName; svc.mPassword = password; svc.mSsl = ssl; + svc.mTrustSsl = trustCertificates; svc.mDeviceId = SyncManager.getDeviceId(); HttpResponse resp = svc.sendHttpClientOptions(); int code = resp.getStatusLine().getStatusCode(); @@ -218,7 +222,12 @@ public class EasSyncService extends AbstractSyncService { throw new MessagingException(MessagingException.IOERROR); } } catch (IOException e) { - userLog("IOException caught, reporting I/O error: ", e.getMessage()); + Throwable cause = e.getCause(); + if (cause != null && cause instanceof CertificateException) { + userLog("CertificateException caught: ", e.getMessage()); + throw new MessagingException(MessagingException.GENERAL_SECURITY); + } + userLog("IOException caught: ", e.getMessage()); throw new MessagingException(MessagingException.IOERROR); } @@ -351,7 +360,7 @@ public class EasSyncService extends AbstractSyncService { mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType=" + mDeviceType; } - String us = (mSsl ? "https" : "http") + "://" + mHostAddress + + String us = (mSsl ? (mTrustSsl ? "httpts" : "https") : "http") + "://" + mHostAddress + "/Microsoft-Server-ActiveSync"; if (cmd != null) { us += "?Cmd=" + cmd + mCmdString; @@ -369,11 +378,17 @@ public class EasSyncService extends AbstractSyncService { method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION); } + private ClientConnectionManager getClientConnectionManager() { + return SyncManager.getClientConnectionManager(); + } + private HttpClient getHttpClient(int timeout) { HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 15*SECONDS); HttpConnectionParams.setSoTimeout(params, timeout); - return new DefaultHttpClient(params); + HttpConnectionParams.setSocketBufferSize(params, 8192); + HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params); + return client; } protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException { diff --git a/src/com/android/exchange/IEmailService.aidl b/src/com/android/exchange/IEmailService.aidl index 23ff74494..a07fab00b 100644 --- a/src/com/android/exchange/IEmailService.aidl +++ b/src/com/android/exchange/IEmailService.aidl @@ -21,7 +21,7 @@ import com.android.exchange.EmailContent; interface IEmailService { int validate(in String protocol, in String host, in String userName, in String password, - int port, boolean ssl) ; + int port, boolean ssl, boolean trustCertificates) ; void startSync(long mailboxId); void stopSync(long mailboxId); diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java index 96196e6c7..3c4cb1359 100644 --- a/src/com/android/exchange/SyncManager.java +++ b/src/com/android/exchange/SyncManager.java @@ -18,6 +18,7 @@ package com.android.exchange; import com.android.email.mail.MessagingException; +import com.android.email.mail.store.TrustManagerFactory; import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.Attachment; @@ -29,6 +30,19 @@ import com.android.email.provider.EmailContent.Message; import com.android.email.provider.EmailContent.SyncColumns; import com.android.exchange.utility.FileLogger; +import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.params.ConnManagerPNames; +import org.apache.http.conn.params.ConnPerRoute; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; + import android.accounts.AccountManager; import android.accounts.OnAccountsUpdatedListener; import android.app.AlarmManager; @@ -65,10 +79,16 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + /** * The SyncManager handles all aspects of starting, maintaining, and stopping the various sync * adapters used by Exchange. However, it is capable of handing any kind of email sync, and it @@ -176,6 +196,8 @@ public class SyncManager extends Service implements Runnable { private static Thread sServiceThread = null; // Cached unique device id private static String sDeviceId = null; + // ConnectionManager that all EAS threads can use + private static ClientConnectionManager sClientConnectionManager = null; private boolean mStop = false; @@ -240,10 +262,10 @@ public class SyncManager extends Service implements Runnable { private final IEmailService.Stub mBinder = new IEmailService.Stub() { public int validate(String protocol, String host, String userName, String password, - int port, boolean ssl) throws RemoteException { + int port, boolean ssl, boolean trustCertificates) throws RemoteException { try { AbstractSyncService.validate(EasSyncService.class, host, userName, password, port, - ssl, SyncManager.this); + ssl, trustCertificates, SyncManager.this); return MessagingException.NO_ERROR; } catch (MessagingException e) { return e.getExceptionType(); @@ -672,6 +694,52 @@ public class SyncManager extends Service implements Runnable { } } + static public ConnPerRoute sConnPerRoute = new ConnPerRoute() { + public int getMaxForRoute(HttpRoute route) { + return 8; + } + }; + + static public synchronized ClientConnectionManager getClientConnectionManager() { + if (sClientConnectionManager == null) { + // Create a registry for our three schemes; http and https will use built-in factories + SchemeRegistry registry = new SchemeRegistry(); + registry.register(new Scheme("http", + PlainSocketFactory.getSocketFactory(), 80)); + registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); + + // Create a new SSLSocketFactory for our "trusted ssl" + // Get the unsecure trust manager from the factory + X509TrustManager trustManager = TrustManagerFactory.get(null, false); + TrustManager[] trustManagers = new TrustManager[] {trustManager}; + SSLContext sslcontext; + try { + sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, trustManagers, null); + SSLContextImpl sslContext = new SSLContextImpl(); + try { + sslContext.engineInit(null, trustManagers, null, null, null); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } + // Ok, now make our factory + SSLSocketFactory sf = new SSLSocketFactory(sslContext.engineGetSocketFactory()); + sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + // Register the httpts scheme with our factory + registry.register(new Scheme("httpts", sf, 443)); + // And create a ccm with our registry + HttpParams params = new BasicHttpParams(); + params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25); + params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute); + sClientConnectionManager = new ThreadSafeClientConnManager(params, registry); + } catch (NoSuchAlgorithmException e2) { + } catch (KeyManagementException e1) { + } + } + // Null is a valid return result if we get an exception + return sClientConnectionManager; + } + @Override public void onDestroy() { log("!!! EAS SyncManager destroyed"); diff --git a/tests/src/com/android/email/activity/setup/AccountSetupExchangeTests.java b/tests/src/com/android/email/activity/setup/AccountSetupExchangeTests.java index f23b075d7..2a77ba94b 100644 --- a/tests/src/com/android/email/activity/setup/AccountSetupExchangeTests.java +++ b/tests/src/com/android/email/activity/setup/AccountSetupExchangeTests.java @@ -38,7 +38,6 @@ public class AccountSetupExchangeTests extends private AccountSetupExchange mActivity; private EditText mServerView; - private EditText mDomainView; private Button mNextButton; public AccountSetupExchangeTests() { @@ -56,7 +55,7 @@ public class AccountSetupExchangeTests extends // This sets up a default URI which can be used by any of the test methods below. // Individual test methods can replace this with a custom URI if they wish // (except those that run on the UI thread - for them, it's too late to change it.) - Intent i = getTestIntent("eas://user:password@server.com/domain"); + Intent i = getTestIntent("eas://user:password@server.com"); setActivityIntent(i); } @@ -64,17 +63,20 @@ public class AccountSetupExchangeTests extends * Test processing with a complete, good URI -> good fields */ public void testGoodUri() { - Intent i = getTestIntent("eas://user:password@server.com/domain"); + Intent i = getTestIntent("eas://user:password@server.com"); setActivityIntent(i); getActivityAndFields(); assertTrue(mNextButton.isEnabled()); } + // TODO Add tests for valid usernames in eas + // They would be or \ or / or a valid email address + /** * No user is not OK - not enabled */ public void testBadUriNoUser() { - Intent i = getTestIntent("eas://:password@server.com/domain"); + Intent i = getTestIntent("eas://:password@server.com"); setActivityIntent(i); getActivityAndFields(); assertFalse(mNextButton.isEnabled()); @@ -84,7 +86,7 @@ public class AccountSetupExchangeTests extends * No password is not OK - not enabled */ public void testBadUriNoPassword() { - Intent i = getTestIntent("eas://user@server.com/domain"); + Intent i = getTestIntent("eas://user@server.com"); setActivityIntent(i); getActivityAndFields(); assertFalse(mNextButton.isEnabled()); @@ -116,49 +118,6 @@ public class AccountSetupExchangeTests extends mServerView.setText("serv$er.com"); assertFalse(mNextButton.isEnabled()); } - - /** - * No EAS domain is OK - enabled - */ - public void testBadUriNoDomain() { - Intent i = getTestIntent("eas://user:password@server.com"); - setActivityIntent(i); - getActivityAndFields(); - assertTrue(mNextButton.isEnabled()); - } - - /** - * Test for non-standard but OK domain names - */ - @UiThreadTest - public void testGoodDomainVariants() { - getActivityAndFields(); - assertTrue(mNextButton.isEnabled()); - - mDomainView.setText(" domain "); - assertTrue(mNextButton.isEnabled()); - } - - /** - * Test for non-empty but non-OK domain names - * - * TODO: These are tests that *should* fail but the code is not checking these cases - */ - @UiThreadTest - public void disabled_testBadDomainVariants() { - getActivityAndFields(); - assertTrue(mNextButton.isEnabled()); - - mDomainView.setText(" "); - assertTrue(mNextButton.isEnabled()); - - mDomainView.setText("do main"); - assertFalse(mNextButton.isEnabled()); - } - - /** - * TODO: More tests of exchange-specific fields? - */ /** * Get the activity (which causes it to be started, using our intent) and get the UI fields @@ -166,7 +125,6 @@ public class AccountSetupExchangeTests extends private void getActivityAndFields() { mActivity = getActivity(); mServerView = (EditText) mActivity.findViewById(R.id.account_server); - mDomainView = (EditText) mActivity.findViewById(R.id.account_domain); mNextButton = (Button) mActivity.findViewById(R.id.next); }