Make sure the client certificate is always installed

b/11678638

Change-Id: Iafe200d14b72678324758fe08b03c8ea7bb9dc5c
diff --git a/src/com/android/exchange/eas/EasConnectionCache.java b/src/com/android/exchange/eas/EasConnectionCache.java
index e4ce7b2..8a21aee 100644
--- a/src/com/android/exchange/eas/EasConnectionCache.java
+++ b/src/com/android/exchange/eas/EasConnectionCache.java
@@ -30,6 +30,7 @@
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpParams;
 
+import java.security.cert.CertificateException;
 import java.util.HashMap;
 
 /**
@@ -84,12 +85,18 @@
      * @return The {@link EmailClientConnectionManager} for hostAuth.
      */
     private EmailClientConnectionManager createConnectionManager(final Context context,
-            final HostAuth hostAuth) {
+            final HostAuth hostAuth)
+            throws CertificateException {
         LogUtils.d(Eas.LOG_TAG, "Creating new connection manager for HostAuth %d", hostAuth.mId);
         final HttpParams params = new BasicHttpParams();
         params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
         params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
-        return EmailClientConnectionManager.newInstance(context, params, hostAuth);
+        final EmailClientConnectionManager mgr =
+                EmailClientConnectionManager.newInstance(context, params, hostAuth);
+
+        mgr.registerClientCert(context, hostAuth);
+
+        return mgr;
     }
 
     /**
@@ -101,7 +108,8 @@
      * @return The {@link EmailClientConnectionManager} for hostAuth.
      */
     private synchronized EmailClientConnectionManager getCachedConnectionManager(
-            final Context context, final HostAuth hostAuth) {
+            final Context context, final HostAuth hostAuth)
+            throws CertificateException {
         EmailClientConnectionManager connectionManager = mConnectionMap.get(hostAuth.mId);
         final long now = System.currentTimeMillis();
         if (connectionManager != null) {
@@ -132,7 +140,8 @@
      * @return The {@link EmailClientConnectionManager} for hostAuth.
      */
     public EmailClientConnectionManager getConnectionManager(
-            final Context context, final HostAuth hostAuth) {
+            final Context context, final HostAuth hostAuth)
+            throws CertificateException {
         final EmailClientConnectionManager connectionManager;
         // We only cache the connection manager for persisted HostAuth objects, i.e. objects
         // whose ids are permanent and won't get reused by other transient HostAuth objects.
diff --git a/src/com/android/exchange/eas/EasOperation.java b/src/com/android/exchange/eas/EasOperation.java
index 37b7ead..92f996f 100644
--- a/src/com/android/exchange/eas/EasOperation.java
+++ b/src/com/android/exchange/eas/EasOperation.java
@@ -45,6 +45,7 @@
 import org.apache.http.entity.ByteArrayEntity;
 
 import java.io.IOException;
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 
 /**
@@ -175,16 +176,7 @@
             // Perform the HTTP request and handle exceptions.
             final EasResponse response;
             try {
-                if (registerClientCert()) {
-                    response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout());
-                } else {
-                    LogUtils.e(LOG_TAG, "Problem registering client cert");
-                    // TODO: Is this the best stat to increment?
-                    if (syncResult != null) {
-                        ++syncResult.stats.numAuthExceptions;
-                    }
-                    return RESULT_CLIENT_CERTIFICATE_REQUIRED;
-                }
+                response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout());
             } catch (final IOException e) {
                 // If we were stopped, return the appropriate result code.
                 switch (mConnection.getStoppedReason()) {
@@ -205,6 +197,14 @@
                     ++syncResult.stats.numIoExceptions;
                 }
                 return RESULT_REQUEST_FAILURE;
+            } catch (final CertificateException e) {
+                LogUtils.i(LOG_TAG, "CertificateException while sending request: %s",
+                        e.getMessage());
+                if (syncResult != null) {
+                    // TODO: Is this the best stat to increment?
+                    ++syncResult.stats.numAuthExceptions;
+                }
+                return RESULT_CLIENT_CERTIFICATE_REQUIRED;
             } catch (final IllegalStateException e) {
                 // Subclasses use ISE to signal a hard error when building the request.
                 // TODO: Switch away from ISEs.
diff --git a/src/com/android/exchange/service/EasAttachmentLoader.java b/src/com/android/exchange/service/EasAttachmentLoader.java
index dd94756..0f3a923 100644
--- a/src/com/android/exchange/service/EasAttachmentLoader.java
+++ b/src/com/android/exchange/service/EasAttachmentLoader.java
@@ -27,6 +27,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.cert.CertificateException;
 
 /**
  * Loads attachments from the Exchange server.
@@ -177,6 +178,10 @@
         } catch (final IOException e) {
             LogUtils.w(TAG, "IOException while loading attachment from server: %s", e.getMessage());
             return null;
+        } catch (final CertificateException e) {
+            LogUtils.w(TAG, "CertificateException while loading attachment from server: %s",
+                    e.getMessage());
+            return null;
         }
     }
 
diff --git a/src/com/android/exchange/service/EasAutoDiscover.java b/src/com/android/exchange/service/EasAutoDiscover.java
index 4f7558e..026e227 100644
--- a/src/com/android/exchange/service/EasAutoDiscover.java
+++ b/src/com/android/exchange/service/EasAutoDiscover.java
@@ -24,6 +24,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URI;
+import java.security.cert.CertificateException;
 
 /**
  * Performs Autodiscover for Exchange servers. This feature tries to find all the configuration
@@ -198,6 +199,9 @@
             resp = executePost(post);
         } catch (final IOException e) {
             return null;
+        } catch (final CertificateException e) {
+            // TODO: Raise this error to the user or something
+            return null;
         }
 
         final int code = resp.getStatus();
diff --git a/src/com/android/exchange/service/EasMeetingResponder.java b/src/com/android/exchange/service/EasMeetingResponder.java
index 2e6efd3..28ef2c6 100644
--- a/src/com/android/exchange/service/EasMeetingResponder.java
+++ b/src/com/android/exchange/service/EasMeetingResponder.java
@@ -28,6 +28,7 @@
 import org.apache.http.HttpStatus;
 
 import java.io.IOException;
+import java.security.cert.CertificateException;
 
 /**
  * Responds to a meeting request, both notifying the EAS server and sending email.
@@ -106,6 +107,8 @@
             responder.sendResponse(msg, mailboxServerId, easResponse);
         } catch (final IOException e) {
             LogUtils.e(TAG, "IOException: %s", e.getMessage());
+        } catch (final CertificateException e) {
+            LogUtils.e(TAG, "CertificateException: %s", e.getMessage());
         }
     }
 
@@ -187,7 +190,7 @@
      * @throws IOException
      */
     private void sendResponse(final Message msg, final String mailboxServerId, final int response)
-            throws IOException {
+            throws IOException, CertificateException {
         final Serializer s = new Serializer();
         s.start(Tags.MREQ_MEETING_RESPONSE).start(Tags.MREQ_REQUEST);
         s.data(Tags.MREQ_USER_RESPONSE, Integer.toString(response));
diff --git a/src/com/android/exchange/service/EasOutboxSyncHandler.java b/src/com/android/exchange/service/EasOutboxSyncHandler.java
index 899719f..c2bff15 100644
--- a/src/com/android/exchange/service/EasOutboxSyncHandler.java
+++ b/src/com/android/exchange/service/EasOutboxSyncHandler.java
@@ -41,6 +41,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 
 /**
@@ -449,6 +450,8 @@
                     resp = sendHttpClientPost(cmd, entity, SEND_MAIL_TIMEOUT);
                 } catch (final IOException e) {
                     return false; // TODO: Handle SyncStatus.FAILURE_IO;
+                } catch (final CertificateException e) {
+                    return false;
                 }
 
             } finally {
diff --git a/src/com/android/exchange/service/EasServerConnection.java b/src/com/android/exchange/service/EasServerConnection.java
index 93faba2..2fb4ebf 100644
--- a/src/com/android/exchange/service/EasServerConnection.java
+++ b/src/com/android/exchange/service/EasServerConnection.java
@@ -148,7 +148,8 @@
         this(context, account, HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv));
     }
 
-    protected EmailClientConnectionManager getClientConnectionManager() {
+    protected EmailClientConnectionManager getClientConnectionManager()
+        throws CertificateException {
         final EmailClientConnectionManager connManager =
                 EasConnectionCache.instance().getConnectionManager(mContext, mHostAuth);
         if (mClientConnectionManager != connManager) {
@@ -169,7 +170,7 @@
         }
     }
 
-    private HttpClient getHttpClient(final long timeout) {
+    private HttpClient getHttpClient(final long timeout) throws CertificateException {
         if (mClient == null) {
             final HttpParams params = new BasicHttpParams();
             HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
@@ -256,7 +257,7 @@
      * @return The {@link EasResponse} from the Exchange server.
      * @throws IOException
      */
-    protected EasResponse sendHttpClientOptions() throws IOException {
+    protected EasResponse sendHttpClientOptions() throws IOException, CertificateException {
         // For OPTIONS, just use the base string and the single header
         final HttpOptions method = new HttpOptions(URI.create(makeBaseUriString()));
         method.setHeader("Authorization", makeAuthString());
@@ -332,7 +333,7 @@
      * @throws IOException
      */
     protected EasResponse sendHttpClientPost(String cmd, final HttpEntity entity,
-            final long timeout) throws IOException {
+            final long timeout) throws IOException, CertificateException {
         final boolean isPingCommand = cmd.equals("Ping");
 
         // Split the mail sending commands
@@ -377,7 +378,7 @@
     }
 
     public EasResponse sendHttpClientPost(final String cmd, final byte[] bytes,
-            final long timeout) throws IOException {
+            final long timeout) throws IOException, CertificateException {
         final ByteArrayEntity entity;
         if (bytes == null) {
             entity = null;
@@ -388,7 +389,7 @@
     }
 
     protected EasResponse sendHttpClientPost(final String cmd, final byte[] bytes)
-            throws IOException {
+            throws IOException, CertificateException {
         return sendHttpClientPost(cmd, bytes, COMMAND_TIMEOUT);
     }
 
@@ -402,7 +403,7 @@
      * @throws IOException
      */
     public EasResponse executeHttpUriRequest(final HttpUriRequest method, final long timeout)
-            throws IOException {
+            throws IOException, CertificateException {
         LogUtils.d(TAG, "EasServerConnection about to make request %s", method.getRequestLine());
         // The synchronized blocks are here to support the stop() function, specifically to handle
         // when stop() is called first. Notably, they are NOT here in order to guard against
@@ -433,7 +434,8 @@
         }
     }
 
-    protected EasResponse executePost(final HttpPost method) throws IOException {
+    protected EasResponse executePost(final HttpPost method)
+            throws IOException, CertificateException {
         return executeHttpUriRequest(method, COMMAND_TIMEOUT);
     }
 
diff --git a/src/com/android/exchange/service/EasSyncHandler.java b/src/com/android/exchange/service/EasSyncHandler.java
index 0aefdb9..674aeda 100644
--- a/src/com/android/exchange/service/EasSyncHandler.java
+++ b/src/com/android/exchange/service/EasSyncHandler.java
@@ -40,6 +40,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.cert.CertificateException;
 
 /**
  * Base class for syncing a single collection from an Exchange server. A "collection" is a single
@@ -336,6 +337,10 @@
             LogUtils.e(TAG, e, "Sync error:");
             syncResult.stats.numIoExceptions++;
             return SYNC_RESULT_FAILED;
+        } catch (final CertificateException e) {
+            LogUtils.e(TAG, e, "Certificate error:");
+            syncResult.stats.numAuthExceptions++;
+            return SYNC_RESULT_FAILED;
         }
 
         final int result;