summaryrefslogtreecommitdiffstats
path: root/emailcommon/src
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2015-03-12 21:07:21 -0700
committerTom Marshall <tdm@cyngn.com>2015-03-12 21:07:21 -0700
commitebaa41f541f99745b5b4884cbe6c71123ec3c3ba (patch)
tree156e860c9f1062f25a2af9cf74d28a2daad910e7 /emailcommon/src
parent8c50ed4589a5cd8f0e9be40ee0cfe71adbd61b10 (diff)
parenta2acc75c6bd263b97656ea8410568e7d38b4040c (diff)
downloadandroid_packages_apps_Email-ebaa41f541f99745b5b4884cbe6c71123ec3c3ba.tar.gz
android_packages_apps_Email-ebaa41f541f99745b5b4884cbe6c71123ec3c3ba.tar.bz2
android_packages_apps_Email-ebaa41f541f99745b5b4884cbe6c71123ec3c3ba.zip
Merge tag 'android-5.1.0_r1' into merge-5.1staging/cm-12.1
Android 5.1.0 release 1 Conflicts: provider_src/com/android/email/provider/AccountReconciler.java provider_src/com/android/email/provider/EmailProvider.java Change-Id: I5b67dce2cbafd7899202c461c607a4fc412d8e62
Diffstat (limited to 'emailcommon/src')
-rwxr-xr-xemailcommon/src/com/android/emailcommon/provider/Account.java14
-rwxr-xr-xemailcommon/src/com/android/emailcommon/provider/EmailContent.java12
-rw-r--r--emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java8
-rw-r--r--emailcommon/src/com/android/emailcommon/service/EmailServiceStatus.java1
-rwxr-xr-xemailcommon/src/com/android/emailcommon/service/IPolicyService.aidl1
-rwxr-xr-xemailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java26
-rw-r--r--emailcommon/src/com/android/emailcommon/service/ServiceProxy.java31
-rw-r--r--emailcommon/src/com/android/emailcommon/utility/SSLSocketFactoryWrapper.java258
-rw-r--r--emailcommon/src/com/android/emailcommon/utility/SSLUtils.java72
9 files changed, 374 insertions, 49 deletions
diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.java b/emailcommon/src/com/android/emailcommon/provider/Account.java
index 513390b69..5a3ab7f3a 100755
--- a/emailcommon/src/com/android/emailcommon/provider/Account.java
+++ b/emailcommon/src/com/android/emailcommon/provider/Account.java
@@ -225,11 +225,17 @@ public final class Account extends EmailContent implements Parcelable {
new String[] {AccountColumns._ID},
AccountColumns.EMAIL_ADDRESS + "=?", new String[] {emailAddress},
null);
- if (c == null || !c.moveToFirst()) {
- return null;
+ try {
+ if (c == null || !c.moveToFirst()) {
+ return null;
+ }
+ final long id = c.getLong(c.getColumnIndex(AccountColumns._ID));
+ return restoreAccountWithId(context, id, observer);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
}
- final long id = c.getLong(c.getColumnIndex(AccountColumns._ID));
- return restoreAccountWithId(context, id, observer);
}
@Override
diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
index 4b2f7b73d..f1fcb0dcf 100755
--- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
+++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
@@ -35,10 +35,10 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.provider.BaseColumns;
-import com.android.emailcommon.utility.TextUtilities;
-import com.android.emailcommon.utility.Utility;
import com.android.emailcommon.Logging;
import com.android.emailcommon.R;
+import com.android.emailcommon.utility.TextUtilities;
+import com.android.emailcommon.utility.Utility;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
@@ -96,9 +96,12 @@ public abstract class EmailContent {
public static final int SYNC_STATUS_USER = UIProvider.SyncStatus.USER_REFRESH;
public static final int SYNC_STATUS_BACKGROUND = UIProvider.SyncStatus.BACKGROUND_SYNC;
public static final int SYNC_STATUS_LIVE = UIProvider.SyncStatus.LIVE_QUERY;
+ public static final int SYNC_STATUS_INITIAL_SYNC_NEEDED =
+ UIProvider.SyncStatus.INITIAL_SYNC_NEEDED;
public static final int LAST_SYNC_RESULT_SUCCESS = UIProvider.LastSyncResult.SUCCESS;
public static final int LAST_SYNC_RESULT_AUTH_ERROR = UIProvider.LastSyncResult.AUTH_ERROR;
+ public static final int LAST_SYNC_RESULT_SERVER_ERROR = UIProvider.LastSyncResult.SERVER_ERROR;
public static final int LAST_SYNC_RESULT_SECURITY_ERROR =
UIProvider.LastSyncResult.SECURITY_ERROR;
public static final int LAST_SYNC_RESULT_CONNECTION_ERROR =
@@ -491,9 +494,6 @@ public abstract class EmailContent {
public static final int CONTENT_SOURCE_KEY_COLUMN = 4;
public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 5;
- private static final String[] PROJECTION_SOURCE_KEY =
- new String[] {BaseColumns._ID, BodyColumns.SOURCE_MESSAGE_KEY};
-
public long mMessageKey;
public String mHtmlContent;
public String mTextContent;
@@ -575,7 +575,7 @@ public abstract class EmailContent {
@VisibleForTesting
public static long restoreBodySourceKey(Context context, long messageId) {
return Utility.getFirstRowLong(context, Body.CONTENT_URI,
- Body.PROJECTION_SOURCE_KEY,
+ new String[] {BodyColumns.SOURCE_MESSAGE_KEY},
BodyColumns.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null,
0, 0L);
}
diff --git a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java
index 1c1f0ebc9..36a0d336e 100644
--- a/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java
+++ b/emailcommon/src/com/android/emailcommon/service/EmailServiceProxy.java
@@ -65,13 +65,13 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
private final boolean isRemote;
// Standard debugging
- public static final int DEBUG_BIT = 1;
+ public static final int DEBUG_BIT = 0x01;
// Verbose (parser) logging
- public static final int DEBUG_VERBOSE_BIT = 2;
+ public static final int DEBUG_EXCHANGE_BIT = 0x02;
// File (SD card) logging
- public static final int DEBUG_FILE_BIT = 4;
+ public static final int DEBUG_FILE_BIT = 0x04;
// Enable strict mode
- public static final int DEBUG_ENABLE_STRICT_MODE = 8;
+ public static final int DEBUG_ENABLE_STRICT_MODE = 0x08;
// The first two constructors are used with local services that can be referenced by class
public EmailServiceProxy(Context _context, Class<?> _class) {
diff --git a/emailcommon/src/com/android/emailcommon/service/EmailServiceStatus.java b/emailcommon/src/com/android/emailcommon/service/EmailServiceStatus.java
index 44922221b..88e5dd29a 100644
--- a/emailcommon/src/com/android/emailcommon/service/EmailServiceStatus.java
+++ b/emailcommon/src/com/android/emailcommon/service/EmailServiceStatus.java
@@ -33,6 +33,7 @@ import android.os.Bundle;
public abstract class EmailServiceStatus {
public static final int SUCCESS = 0;
public static final int IN_PROGRESS = 1;
+ public static final int FAILURE = 2;
public static final int MESSAGE_NOT_FOUND = 0x10;
public static final int ATTACHMENT_NOT_FOUND = 0x11;
diff --git a/emailcommon/src/com/android/emailcommon/service/IPolicyService.aidl b/emailcommon/src/com/android/emailcommon/service/IPolicyService.aidl
index c284292e0..2c2c9903a 100755
--- a/emailcommon/src/com/android/emailcommon/service/IPolicyService.aidl
+++ b/emailcommon/src/com/android/emailcommon/service/IPolicyService.aidl
@@ -25,4 +25,5 @@ interface IPolicyService {
// New version
void setAccountPolicy2(long accountId, in Policy policy, String securityKey, boolean notify);
oneway void remoteWipe();
+ boolean canDisableCamera();
} \ No newline at end of file
diff --git a/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java
index fcd916f25..c5dd6c180 100755
--- a/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java
+++ b/emailcommon/src/com/android/emailcommon/service/PolicyServiceProxy.java
@@ -85,6 +85,24 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
waitForCompletion();
}
+ public boolean canDisableCamera() throws RemoteException {
+ setTask(new ProxyTask() {
+ @Override
+ public void run() throws RemoteException {
+ mReturn = mService.canDisableCamera();
+ }
+ }, "canDisableCamera");
+ waitForCompletion();
+ if (mReturn == null) {
+ // This is not a great situation, but it's better to act like the policy isn't enforced
+ // rather than crash.
+ LogUtils.e(TAG, "PolicyService unavailable in canDisableCamera; assuming false");
+ return false;
+ } else {
+ return (Boolean)mReturn;
+ }
+ }
+
@Override
public void remoteWipe() throws RemoteException {
setTask(new ProxyTask() {
@@ -145,5 +163,13 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
}
throw new IllegalStateException("PolicyService transaction failed");
}
+
+ public static boolean canDisableCamera(Context context) {
+ try {
+ return new PolicyServiceProxy(context).canDisableCamera();
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
}
diff --git a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
index 1c4f3b6bb..3669345c1 100644
--- a/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
+++ b/emailcommon/src/com/android/emailcommon/service/ServiceProxy.java
@@ -114,19 +114,24 @@ public abstract class ServiceProxy {
try {
mTask.run();
} catch (RemoteException e) {
- }
- try {
- // Each ServiceProxy handles just one task, so we unbind after we're
- // done with our work.
- mContext.unbindService(mConnection);
- } catch (RuntimeException e) {
- // The exceptions that are thrown here look like IllegalStateException,
- // IllegalArgumentException and RuntimeException. Catching RuntimeException
- // which get them all. Reasons for these exceptions include services that
- // have already been stopped or unbound. This can happen if the user ended
- // the activity that was using the service. This is harmless, but we've got
- // to catch it.
- LogUtils.e(mTag, e, "RuntimeException when trying to unbind from service");
+ LogUtils.e(mTag, e, "RemoteException thrown running mTask!");
+ } finally {
+ // Make sure that we unbind the mConnection even on exceptions in the
+ // task provided by the subclass.
+ try {
+ // Each ServiceProxy handles just one task, so we unbind after we're
+ // done with our work.
+ mContext.unbindService(mConnection);
+ } catch (RuntimeException e) {
+ // The exceptions that are thrown here look like IllegalStateException,
+ // IllegalArgumentException and RuntimeException. Catching
+ // RuntimeException which get them all. Reasons for these exceptions
+ // include services that have already been stopped or unbound. This can
+ // happen if the user ended the activity that was using the service.
+ // This is harmless, but we've got to catch it.
+ LogUtils.e(mTag, e,
+ "RuntimeException when trying to unbind from service");
+ }
}
mTaskCompleted = true;
synchronized(mConnection) {
diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactoryWrapper.java b/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactoryWrapper.java
new file mode 100644
index 000000000..66b596bff
--- /dev/null
+++ b/emailcommon/src/com/android/emailcommon/utility/SSLSocketFactoryWrapper.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.emailcommon.utility;
+
+import com.android.mail.utils.LogUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.*;
+import javax.net.ssl.SSLSocketFactory;
+
+public class SSLSocketFactoryWrapper extends javax.net.ssl.SSLSocketFactory {
+ private final SSLSocketFactory mFactory;
+ private final boolean mSecure;
+ private final int mHandshakeTimeout;
+ private final String[] mDefaultCipherSuites;
+
+ private final String[] DEPRECATED_CIPHER_SUITES_TO_ENABLE = new String[] {
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDH_RSA_WITH_RC4_128_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "SSL_RSA_WITH_DES_CBC_SHA"
+ };
+
+ SSLSocketFactoryWrapper(final SSLSocketFactory factory, final boolean secure,
+ int handshakeTimeout) {
+ mFactory = factory;
+ mSecure = secure;
+ mHandshakeTimeout = handshakeTimeout;
+
+ // Find the base factory's list of defaultCipherSuites, and merge our extras with it.
+ // Remember that the order is important. We'll add our extras at the end, and only
+ // if they weren't already in the base factory's list.
+ final String[] baseDefaultCipherSuites = mFactory.getDefaultCipherSuites();
+ final List<String> fullCipherSuiteList = new ArrayList<String>(Arrays.asList(
+ mFactory.getDefaultCipherSuites()));
+ final Set<String> baseDefaultCipherSuiteSet = new HashSet<String>(fullCipherSuiteList);
+
+ final String[] baseSupportedCipherSuites = mFactory.getSupportedCipherSuites();
+ final Set<String> baseSupportedCipherSuiteSet = new HashSet<String>(Arrays.asList(
+ mFactory.getSupportedCipherSuites()));
+
+ for (String cipherSuite : DEPRECATED_CIPHER_SUITES_TO_ENABLE) {
+ if (baseSupportedCipherSuiteSet.contains(cipherSuite) &&
+ !baseDefaultCipherSuiteSet.contains(cipherSuite)) {
+ fullCipherSuiteList.add(cipherSuite);
+ }
+ }
+ mDefaultCipherSuites = new String[fullCipherSuiteList.size()];
+ fullCipherSuiteList.toArray(mDefaultCipherSuites);
+ }
+
+ public static SSLSocketFactory getDefault(final KeyManager[] keyManagers, int handshakeTimeout)
+ throws NoSuchAlgorithmException, KeyManagementException{
+ final SSLContext context = SSLContext.getInstance("TLS");
+ context.init(keyManagers, null, null);
+ return new SSLSocketFactoryWrapper(context.getSocketFactory(), true, handshakeTimeout);
+ }
+
+ public static SSLSocketFactory getInsecure(final KeyManager[] keyManagers,
+ final TrustManager[] trustManagers,
+ int handshakeTimeout)
+ throws NoSuchAlgorithmException, KeyManagementException {
+ final SSLContext context = SSLContext.getInstance("TLS");
+ context.init(keyManagers, trustManagers, null);
+ return new SSLSocketFactoryWrapper(context.getSocketFactory(), false, handshakeTimeout);
+ }
+
+ public Socket createSocket()throws IOException {
+ return mFactory.createSocket();
+ }
+
+ public Socket createSocket(final Socket socket, final String host, final int port,
+ final boolean autoClose) throws IOException {
+ final SSLSocket sslSocket = (SSLSocket)mFactory.createSocket(socket, host, port, autoClose);
+ setHandshakeTimeout(sslSocket, mHandshakeTimeout);
+ sslSocket.setEnabledCipherSuites(mDefaultCipherSuites);
+ if (mSecure) {
+ verifyHostname(sslSocket, host);
+ }
+ return sslSocket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ final SSLSocket sslSocket = (SSLSocket)mFactory.createSocket(host, port);
+ setHandshakeTimeout(sslSocket, mHandshakeTimeout);
+ sslSocket.setEnabledCipherSuites(mDefaultCipherSuites);
+ if (mSecure) {
+ verifyHostname(sslSocket, host);
+ }
+ return sslSocket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int i, InetAddress inetAddress, int i2) throws
+ IOException, UnknownHostException {
+ final SSLSocket sslSocket = (SSLSocket)mFactory.createSocket(host, i, inetAddress, i2);
+ setHandshakeTimeout(sslSocket, mHandshakeTimeout);
+ sslSocket.setEnabledCipherSuites(mDefaultCipherSuites);
+ if (mSecure) {
+ verifyHostname(sslSocket, host);
+ }
+ return sslSocket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
+ final SSLSocket sslSocket = (SSLSocket)mFactory.createSocket(inetAddress, i);
+ setHandshakeTimeout(sslSocket, mHandshakeTimeout);
+ sslSocket.setEnabledCipherSuites(mDefaultCipherSuites);
+ return sslSocket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress2, int i2)
+ throws IOException {
+ final SSLSocket sslSocket = (SSLSocket)mFactory.createSocket(inetAddress, i, inetAddress2,
+ i2);
+ setHandshakeTimeout(sslSocket, mHandshakeTimeout);
+ sslSocket.setEnabledCipherSuites(mDefaultCipherSuites);
+ return sslSocket;
+ }
+
+ public String[] getDefaultCipherSuites() {
+ return mDefaultCipherSuites.clone();
+ }
+
+ public String[] getSupportedCipherSuites() {
+ return mFactory.getSupportedCipherSuites();
+ }
+
+ /**
+ * Attempt to set the hostname of the socket.
+ * @param sslSocket The SSLSocket
+ * @param hostname the hostname
+ * @return true if able to set the hostname, false if not.
+ */
+ public static boolean potentiallyEnableSni(SSLSocket sslSocket, String hostname) {
+ try {
+ // Many implementations of SSLSocket support setHostname, although it is not part of
+ // the class definition. We will attempt to setHostname using reflection. If the
+ // particular SSLSocket implementation we are using does not support this meethod,
+ // we'll fail and return false.
+ sslSocket.getClass().getMethod("setHostname", String.class).invoke(sslSocket, hostname);
+ return true;
+ } catch (Exception ignored) {
+ return false;
+ }
+ }
+
+ /**
+ * Attempt to enable session tickets.
+ * @param sslSocket the SSLSocket.
+ * @return true if able to enable session tickets, false otherwise.
+ */
+ public static boolean potentiallyEnableSessionTickets(SSLSocket sslSocket) {
+ try {
+ // Many implementations of SSLSocket support setUseSessionTickets, although it is not
+ // part of the class definition. We will attempt to setHostname using reflection. If the
+ // particular SSLSocket implementation we are using does not support this meethod,
+ // we'll fail and return false.
+ sslSocket.getClass().getMethod("setUseSessionTickets", boolean.class)
+ .invoke(sslSocket, true);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * Verify the hostname of the certificate used by the other end of a
+ * connected socket. You MUST call this if you did not supply a hostname
+ * to {@link #createSocket()}. It is harmless to call this method
+ * redundantly if the hostname has already been verified.
+ *
+ * @param socket An SSL socket which has been connected to a server
+ * @param hostname The expected hostname of the remote server
+ * @throws IOException if something goes wrong handshaking with the server
+ * @throws SSLPeerUnverifiedException if the server cannot prove its identity
+ *
+ * @hide
+ */
+ public static void verifyHostname(Socket socket, String hostname) throws IOException {
+ if (!(socket instanceof SSLSocket)) {
+ throw new IllegalArgumentException("Attempt to verify non-SSL socket");
+ }
+
+ // The code at the start of OpenSSLSocketImpl.startHandshake()
+ // ensures that the call is idempotent, so we can safely call it.
+ SSLSocket ssl = (SSLSocket) socket;
+ ssl.startHandshake();
+
+ SSLSession session = ssl.getSession();
+ if (session == null) {
+ throw new SSLException("Cannot verify SSL socket without session");
+ }
+ LogUtils.d(LogUtils.TAG, "using cipherSuite %s", session.getCipherSuite());
+ if (!HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session)) {
+ throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
+ }
+ }
+
+ private void setHandshakeTimeout(SSLSocket sslSocket, int timeout) {
+ try {
+ // Most implementations of SSLSocket support setHandshakeTimeout(), but it is not
+ // actually part of the class definition. We will attempt to set it using reflection.
+ // If the particular implementation of SSLSocket we are using does not support this
+ // function, then we will just have to use the default handshake timeout.
+ sslSocket.getClass().getMethod("setHandshakeTimeout", int.class).invoke(sslSocket,
+ timeout);
+ } catch (Exception e) {
+ LogUtils.w(LogUtils.TAG, e, "unable to set handshake timeout");
+ }
+ }
+}
diff --git a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java
index 33ddde7dc..49963bc3c 100644
--- a/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java
+++ b/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java
@@ -20,7 +20,6 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.net.SSLCertificateSocketFactory;
import android.security.KeyChain;
import android.security.KeyChainException;
@@ -33,6 +32,8 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -49,7 +50,7 @@ import javax.net.ssl.X509TrustManager;
public class SSLUtils {
// All secure factories are the same; all insecure factories are associated with HostAuth's
- private static SSLCertificateSocketFactory sSecureFactory;
+ private static javax.net.ssl.SSLSocketFactory sSecureFactory;
private static final boolean LOG_ENABLED = false;
private static final String TAG = "Email.Ssl";
@@ -137,40 +138,67 @@ public class SSLUtils {
}
}
+ public static abstract class ExternalSecurityProviderInstaller {
+ abstract public void installIfNeeded(final Context context);
+ }
+
+ private static ExternalSecurityProviderInstaller sExternalSecurityProviderInstaller;
+
+ public static void setExternalSecurityProviderInstaller (
+ ExternalSecurityProviderInstaller installer) {
+ sExternalSecurityProviderInstaller = installer;
+ }
+
/**
* Returns a {@link javax.net.ssl.SSLSocketFactory}.
* Optionally bypass all SSL certificate checks.
*
* @param insecure if true, bypass all SSL certificate checks
*/
- public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context,
- HostAuth hostAuth, boolean insecure) {
- if (insecure) {
- SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory)
- SSLCertificateSocketFactory.getInsecure(SSL_HANDSHAKE_TIMEOUT, null);
- insecureFactory.setTrustManagers(
- new TrustManager[] {
- new SameCertificateCheckingTrustManager(context, hostAuth)});
- return insecureFactory;
- } else {
- if (sSecureFactory == null) {
- sSecureFactory = (SSLCertificateSocketFactory)
- SSLCertificateSocketFactory.getDefault(SSL_HANDSHAKE_TIMEOUT, null);
+ public synchronized static javax.net.ssl.SSLSocketFactory getSSLSocketFactory(
+ final Context context, final HostAuth hostAuth, final KeyManager keyManager,
+ final boolean insecure) {
+ // If we have an external security provider installer, then install. This will
+ // potentially replace the default implementation of SSLSocketFactory.
+ if (sExternalSecurityProviderInstaller != null) {
+ sExternalSecurityProviderInstaller.installIfNeeded(context);
+ }
+ try {
+ final KeyManager[] keyManagers = (keyManager == null ? null :
+ new KeyManager[]{keyManager});
+ if (insecure) {
+ final TrustManager[] trustManagers = new TrustManager[]{
+ new SameCertificateCheckingTrustManager(context, hostAuth)};
+ SSLSocketFactoryWrapper insecureFactory =
+ (SSLSocketFactoryWrapper) SSLSocketFactoryWrapper.getInsecure(
+ keyManagers, trustManagers, SSL_HANDSHAKE_TIMEOUT);
+ return insecureFactory;
+ } else {
+ if (sSecureFactory == null) {
+ SSLSocketFactoryWrapper secureFactory =
+ (SSLSocketFactoryWrapper) SSLSocketFactoryWrapper.getDefault(
+ keyManagers, SSL_HANDSHAKE_TIMEOUT);
+ sSecureFactory = secureFactory;
+ }
+ return sSecureFactory;
}
- return sSecureFactory;
+ } catch (NoSuchAlgorithmException e) {
+ LogUtils.wtf(TAG, e, "Unable to acquire SSLSocketFactory");
+ // TODO: what can we do about this?
+ } catch (KeyManagementException e) {
+ LogUtils.wtf(TAG, e, "Unable to acquire SSLSocketFactory");
+ // TODO: what can we do about this?
}
+ return null;
}
/**
- * Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
- * Apache HTTP stack.
+ * Returns a com.android.emailcommon.utility.SSLSocketFactory
*/
public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth,
KeyManager keyManager, boolean insecure) {
- SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure);
- if (keyManager != null) {
- underlying.setKeyManagers(new KeyManager[] { keyManager });
- }
+ javax.net.ssl.SSLSocketFactory underlying = getSSLSocketFactory(context, hostAuth,
+ keyManager, insecure);
SSLSocketFactory wrapped = new SSLSocketFactory(underlying);
if (insecure) {
wrapped.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);