Refactor code that talks to EAS server to new base class.

Two reasons for this:
1) It lets us decouple Ping from the SyncHandler family.
2) Account validation also needs these functions but
   shouldn't be a SyncHandler.

Change-Id: I0c56ead551d3c3cdc34c2c78e3bad62fad8be25b
diff --git a/src/com/android/exchange/service/EasPingSyncHandler.java b/src/com/android/exchange/service/EasPingSyncHandler.java
index 9a8dc57..da72797 100644
--- a/src/com/android/exchange/service/EasPingSyncHandler.java
+++ b/src/com/android/exchange/service/EasPingSyncHandler.java
@@ -2,7 +2,6 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SyncResult;
 import android.database.Cursor;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -24,26 +23,18 @@
 import com.android.exchange.adapter.Tags;
 
 import org.apache.http.HttpStatus;
-import org.apache.http.entity.ByteArrayEntity;
 
 import java.io.IOException;
 import java.util.ArrayList;
 
 /**
  * Performs an Exchange Ping, which is the command for receiving push notifications.
- * That this extends EasSyncHandler is a bit wonky -- it's not really intended to handle SyncManager
- * requests, which is what that base class is primarily set up for. However, it also is the home
- * of all the functions that actually talk to the EAS server, so for now, this is where it lives.
- *
- * There are two parts to Pinging:
- * 1) Altering an account's ping (start, stop, and changing which folders are pinged). This is
- *    done in performSync. It's odd that performSync just sets the ping up, and doesn't run it,
- *    but since performSync is one and only external call into this class, that's got to be how it
- *    works.
- * 2) Actually waiting on the hanging POST. This is run in a separate thread that is triggered in
- *    performSync.
+ * TODO: Rename this, now that it's no longer a subclass of EasSyncHandler.
  */
-public class EasPingSyncHandler extends EasSyncHandler {
+public class EasPingSyncHandler extends EasServerConnection {
+    private final ContentResolver mContentResolver;
+    private final Account mAccount;
+    private final HostAuth mHostAuth;
     private final PingTask mPingTask;
 
     private class PingTask extends AsyncTask<Void, Void, Void> {
@@ -96,8 +87,8 @@
                         // in handleOneMailbox when the Serializer is first created.
                         // If either side changes, the other must be kept in sync.
                         s.end().end().done();
-                        final EasResponse resp = sendHttpClientPost("Ping",
-                                new ByteArrayEntity(s.toByteArray()), PING_HEARTBEAT);
+                        final EasResponse resp = sendHttpClientPost(mAccount, mHostAuth, "Ping",
+                                s.toByteArray(), PING_HEARTBEAT);
                         try {
                             continuePing = handleResponse(resp, amAccount);
                         } finally {
@@ -296,30 +287,20 @@
 
     }
 
-    public EasPingSyncHandler(final Context context, final ContentResolver contentResolver,
-            final Account account, final Bundle syncExtras, final SyncResult syncResult,
+    public EasPingSyncHandler(final Context context, final Account account,
             final EmailSyncAdapterService.SyncHandlerSychronizer syncHandlerMap) {
-        super(context, contentResolver, account, null, syncExtras, syncResult);
+        super(context);
+        mContentResolver = context.getContentResolver();
+        mAccount = account;
+        mHostAuth = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
         mPingTask = new PingTask(syncHandlerMap);
+        mPingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
     @Override
-    public SyncStatus performSync() {
-        // Create and start the task.
-        mPingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-        return SyncStatus.SUCCESS;
-    }
-
     public synchronized void stop() {
-        if (mPendingPost != null) {
-            mPendingPost.abort();
-        } else {
-            // TODO: Improve handing if stop() is called after the POST returns.
-            // For now, this works because it will interrupt the following loop. It's sub-optimal
-            // for refresh, though.
-            mStopped = true;
-        }
-        // TODO: Consider also interrupting mPingTask in the non-refresh case (or alter refresh
-        // to restart the task).
+        // TODO: Consider also interrupting mPingTask in the non-refresh case, or alter refresh
+        // to restart the task. (Yes, this override currently exists purely for this comment.)
+        super.stop();
     }
 }
diff --git a/src/com/android/exchange/service/EasServerConnection.java b/src/com/android/exchange/service/EasServerConnection.java
new file mode 100644
index 0000000..671a548
--- /dev/null
+++ b/src/com/android/exchange/service/EasServerConnection.java
@@ -0,0 +1,260 @@
+package com.android.exchange.service;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Base64;
+
+import com.android.emailcommon.Device;
+import com.android.emailcommon.provider.Account;
+import com.android.emailcommon.provider.HostAuth;
+import com.android.emailcommon.utility.EmailClientConnectionManager;
+import com.android.exchange.Eas;
+import com.android.exchange.EasResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+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.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Base class for communicating with an EAS server. Anything that needs to send messages to the
+ * server can subclass this to get access to the {@link #sendHttpClientPost} family of functions.
+ */
+public abstract class EasServerConnection {
+    /**
+     * Timeout for establishing a connection to the server.
+     */
+    private static final long CONNECTION_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
+
+    /**
+     * Timeout for http requests after the connection has been established.
+     */
+    protected static final long COMMAND_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
+
+    private static final String DEVICE_TYPE = "Android";
+    private static final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' +
+        Eas.CLIENT_VERSION;
+
+
+    private static final ConnPerRoute sConnPerRoute = new ConnPerRoute() {
+        @Override
+        public int getMaxForRoute(final HttpRoute route) {
+            return 8;
+        }
+    };
+
+    protected final Context mContext;
+
+    // Bookkeeping for interrupting a POST. This is primarily for use by Ping (there's currently
+    // no mechanism for stopping a sync).
+    // Access to these variables should be synchronized on this.
+    private HttpPost mPendingPost = null;
+    private boolean mStopped = false;
+
+    protected EasServerConnection(final Context context) {
+        mContext = context;
+    }
+
+    // TODO: Don't make a new one each time.
+    private EmailClientConnectionManager getClientConnectionManager(final HostAuth hostAuth) {
+        final HttpParams params = new BasicHttpParams();
+        params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
+        params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
+        final boolean ssl = hostAuth.shouldUseSsl();
+        final int port = hostAuth.mPort;
+        return EmailClientConnectionManager.newInstance(mContext, params, hostAuth);
+    }
+
+    private HttpClient getHttpClient(final EmailClientConnectionManager connectionManager,
+            final long timeout) {
+        final HttpParams params = new BasicHttpParams();
+        HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
+        HttpConnectionParams.setSoTimeout(params, (int)(timeout));
+        HttpConnectionParams.setSocketBufferSize(params, 8192);
+        return new DefaultHttpClient(connectionManager, params);
+    }
+
+    private String makeAuthString(final HostAuth hostAuth) {
+        final String cs = hostAuth.mLogin + ":" + hostAuth.mPassword;
+        return "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
+    }
+
+    private String makeUserString(final HostAuth hostAuth) {
+        String deviceId = "";
+        try {
+            deviceId = Device.getDeviceId(mContext);
+        } catch (final IOException e) {
+            // TODO: Make Device.getDeviceId not throw IOException, if possible.
+            // Otherwise use a better deviceId default.
+            deviceId = "0";
+        }
+        return "&User=" + Uri.encode(hostAuth.mLogin) + "&DeviceId=" +
+                deviceId + "&DeviceType=" + DEVICE_TYPE;
+    }
+
+    private String makeBaseUriString(final HostAuth hostAuth) {
+        return EmailClientConnectionManager.makeScheme(hostAuth.shouldUseSsl(),
+                hostAuth.shouldTrustAllServerCerts(), hostAuth.mClientCertAlias) +
+                "://" + hostAuth.mAddress + "/Microsoft-Server-ActiveSync";
+    }
+
+    private String makeUriString(final HostAuth hostAuth, final String cmd, final String extra) {
+        String uriString = makeBaseUriString(hostAuth);
+        if (cmd != null) {
+            uriString += "?Cmd=" + cmd + makeUserString(hostAuth);
+        }
+        if (extra != null) {
+            uriString += extra;
+        }
+        return uriString;
+    }
+
+    /**
+     * Get the protocol version for an account, or a default if we can't determine it.
+     * @param account The account whose protocol version we want to get.
+     * @return The protocol version for account, as a String.
+     */
+    protected String getProtocolVersion(final Account account) {
+        if (account != null && account.mProtocolVersion != null) {
+            return account.mProtocolVersion;
+        }
+        return Eas.DEFAULT_PROTOCOL_VERSION;
+    }
+
+    /**
+     * Set standard HTTP headers, using a policy key if required
+     * @param account The Account for which we are communicating.
+     * @param hostAuth the HostAuth for account.
+     * @param method the method we are going to send
+     * @param usePolicyKey whether or not a policy key should be sent in the headers
+     */
+    private void setHeaders(final Account account, final HostAuth hostAuth,
+            final HttpRequestBase method, final boolean usePolicyKey) {
+        method.setHeader("Authorization", makeAuthString(hostAuth));
+        method.setHeader("MS-ASProtocolVersion", getProtocolVersion(account));
+        method.setHeader("User-Agent", USER_AGENT);
+        method.setHeader("Accept-Encoding", "gzip");
+        if (usePolicyKey) {
+            // If there's an account in existence, use its key; otherwise (we're creating the
+            // account), send "0".  The server will respond with code 449 if there are policies
+            // to be enforced
+            String key = "0";
+            if (account != null) {
+                final String accountKey = account.mSecuritySyncKey;
+                if (!TextUtils.isEmpty(accountKey)) {
+                    key = accountKey;
+                }
+            }
+            method.setHeader("X-MS-PolicyKey", key);
+        }
+    }
+
+    /**
+     * Send a POST request to the server.
+     * @param account The {@link Account} for which we're sending the POST.
+     * @param hostAuth The {@link HostAuth} for account.
+     * @param cmd The command we're sending to the server.
+     * @param entity The {@link HttpEntity} containing the payload of the message.
+     * @param timeout The timeout for this POST.
+     * @return The response from the Exchange server.
+     * @throws IOException
+     */
+    protected EasResponse sendHttpClientPost(final Account account, final HostAuth hostAuth,
+            String cmd, final HttpEntity entity, final long timeout) throws IOException {
+        final EmailClientConnectionManager connectionManager = getClientConnectionManager(hostAuth);
+        final HttpClient client = getHttpClient(connectionManager, timeout);
+        final boolean isPingCommand = cmd.equals("Ping");
+
+        // Split the mail sending commands
+        String extra = null;
+        boolean msg = false;
+        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
+            final int cmdLength = cmd.indexOf('&');
+            extra = cmd.substring(cmdLength);
+            cmd = cmd.substring(0, cmdLength);
+            msg = true;
+        } else if (cmd.startsWith("SendMail&")) {
+            msg = true;
+        }
+
+        final String us = makeUriString(hostAuth, cmd, extra);
+        final HttpPost method = new HttpPost(URI.create(us));
+        // Send the proper Content-Type header; it's always wbxml except for messages when
+        // the EAS protocol version is < 14.0
+        // If entity is null (e.g. for attachments), don't set this header
+        final String protocolVersion = getProtocolVersion(account);
+        final Double protocolVersionDouble = Eas.getProtocolVersionDouble(protocolVersion);
+        if (msg && (protocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) {
+            method.setHeader("Content-Type", "message/rfc822");
+        } else if (entity != null) {
+            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
+        }
+        setHeaders(account, hostAuth, method, !isPingCommand);
+        // NOTE
+        // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate
+        // network activity related to the Ping command on some networks with some servers.
+        // This code should be removed when the underlying issue is resolved
+        if (isPingCommand) {
+            method.setHeader("Connection", "close");
+        }
+        method.setEntity(entity);
+
+        synchronized (this) {
+            if (mStopped) {
+                mStopped = false;
+                // If this gets stopped after the POST actually starts, it throws an IOException.
+                // Therefore if we get stopped here, let's throw the same sort of exception, so
+                // callers can just equate IOException with the "this POST got killed for some
+                // reason".
+                throw new IOException("Sync was stopped before POST");
+            }
+           mPendingPost = method;
+        }
+        try {
+            return EasResponse.fromHttpRequest(connectionManager, client, method);
+        } finally {
+            synchronized (this) {
+                mPendingPost = null;
+            }
+        }
+    }
+
+    protected EasResponse sendHttpClientPost(final Account account, final HostAuth hostAuth,
+            final String cmd, final byte[] bytes, final long timeout) throws IOException {
+        return sendHttpClientPost(account, hostAuth, cmd, new ByteArrayEntity(bytes), timeout);
+    }
+
+    protected EasResponse sendHttpClientPost(final Account account, final HostAuth hostAuth,
+            final String cmd, final byte[] bytes) throws IOException {
+        return sendHttpClientPost(account, hostAuth, cmd, bytes, COMMAND_TIMEOUT);
+    }
+
+    /**
+     * Stop the current request. If we're in the middle of the POST, abort it, otherwise prevent
+     * the next POST from happening. This second part is necessary in cases where the stop request
+     * happens while we're setting up the POST but before we're actually in it.
+     * TODO: We also want to do something reasonable if the stop request comes in after the POST
+     * responds.
+     */
+    public synchronized void stop() {
+        if (mPendingPost != null) {
+            mPendingPost.abort();
+        } else {
+            mStopped = true;
+        }
+    }
+}
diff --git a/src/com/android/exchange/service/EasSyncHandler.java b/src/com/android/exchange/service/EasSyncHandler.java
index b75efdc..aa482d1 100644
--- a/src/com/android/exchange/service/EasSyncHandler.java
+++ b/src/com/android/exchange/service/EasSyncHandler.java
@@ -3,50 +3,25 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SyncResult;
-import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Base64;
 
-import com.android.emailcommon.Device;
 import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.HostAuth;
 import com.android.emailcommon.provider.Mailbox;
 import com.android.emailcommon.service.EmailServiceStatus;
-import com.android.emailcommon.utility.EmailClientConnectionManager;
-import com.android.exchange.Eas;
 import com.android.exchange.EasResponse;
 
 import org.apache.http.HttpEntity;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-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.entity.ByteArrayEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
 
 import java.io.IOException;
-import java.net.URI;
 
 /**
  * Base class for performing a single sync action. It holds the state needed for all sync actions
- * (e.g. account and auth info, sync extras and results), commonly used functions to handle the
- * network communication with the server, and functions to communicate to with the app UI.
+ * (e.g. account and auth info, sync extras and results) and functions to communicate to with the
+ * app UI.
  * Sublclasses must implement {@link #performSync}, but otherwise have no other requirements.
  */
-public abstract class EasSyncHandler {
-    private static final String DEVICE_TYPE = "Android";
-    private static final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' +
-        Eas.CLIENT_VERSION;
-
-    protected final Context mContext;
+public abstract class EasSyncHandler extends EasServerConnection {
     protected final ContentResolver mContentResolver;
     protected final Account mAccount;
     protected final HostAuth mHostAuth;
@@ -54,14 +29,9 @@
     protected final Bundle mSyncExtras;
     protected final SyncResult mSyncResult;
 
-    // Bookkeeping for interrupting a sync. This is primarily for use by Ping (there's currently
-    // no mechanism for stopping a sync).
-    // Access to these variables should be synchronized on this.
-    protected HttpPost mPendingPost = null;
-    protected boolean mStopped = false;
-
     /**
      * Status values to indicate success or manner of failure when sending a single Message.
+     * TODO: eliminate this enum, possibly in favor of {@link EmailServiceStatus} values.
      */
     public enum SyncStatus {
         // Message sent successfully.
@@ -80,7 +50,7 @@
     protected EasSyncHandler(final Context context, final ContentResolver contentResolver,
             final Account account, final Mailbox mailbox, final Bundle syncExtras,
             final SyncResult syncResult) {
-        mContext = context;
+        super(context);
         mContentResolver = contentResolver;
         mAccount = account;
         mHostAuth = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
@@ -133,175 +103,20 @@
      */
     public abstract SyncStatus performSync();
 
-
-    // Client - server communication.
-
-    // Command timeout is the the time allowed for reading data from an open connection before an
-    // IOException is thrown.  After a small added allowance, our watchdog alarm goes off (allowing
-    // us to detect a silently dropped connection).  The allowance is defined below.
-    protected static final long COMMAND_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
-    // Connection timeout is the time given to connect to the server before reporting an IOException
-    protected static final long CONNECTION_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
-
-    private static final ConnPerRoute sConnPerRoute = new ConnPerRoute() {
-        @Override
-        public int getMaxForRoute(final HttpRoute route) {
-            return 8;
-        }
-    };
-
-    // TODO: Don't make a new one each time.
-    private EmailClientConnectionManager getClientConnectionManager() {
-        final HttpParams params = new BasicHttpParams();
-        params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
-        params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
-        final boolean ssl = mHostAuth.shouldUseSsl();
-        final int port = mHostAuth.mPort;
-        return EmailClientConnectionManager.newInstance(mContext, params, mHostAuth);
-    }
-
-    private HttpClient getHttpClient(final long timeout) {
-        final HttpParams params = new BasicHttpParams();
-        HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
-        HttpConnectionParams.setSoTimeout(params, (int)(timeout));
-        HttpConnectionParams.setSocketBufferSize(params, 8192);
-        return new DefaultHttpClient(getClientConnectionManager(), params);
-    }
-
-    private String makeAuthString() {
-        final String cs = mHostAuth.mLogin + ":" + mHostAuth.mPassword;
-        return "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
-    }
-
-    private String makeUserString() {
-        String deviceId = "";
-        try {
-            deviceId = Device.getDeviceId(mContext);
-        } catch (final IOException e) {
-            // TODO: Make Device.getDeviceId not throw IOException, if possible.
-            // Otherwise use a better deviceId default.
-            deviceId = "0";
-        }
-        return "&User=" + Uri.encode(mHostAuth.mLogin) + "&DeviceId=" +
-                deviceId + "&DeviceType=" + DEVICE_TYPE;
-    }
-
-    private String makeBaseUriString() {
-        return EmailClientConnectionManager.makeScheme(mHostAuth.shouldUseSsl(),
-                mHostAuth.shouldTrustAllServerCerts(), mHostAuth.mClientCertAlias) +
-                "://" + mHostAuth.mAddress + "/Microsoft-Server-ActiveSync";
-    }
-
-    private String makeUriString(final String cmd, final String extra) {
-        String uriString = makeBaseUriString();
-        if (cmd != null) {
-            uriString += "?Cmd=" + cmd + makeUserString();
-        }
-        if (extra != null) {
-            uriString += extra;
-        }
-        return uriString;
-    }
+    // Convenience versions of EasServerConnection functions, using our member variables.
 
     protected String getProtocolVersion() {
-        if (mAccount != null && mAccount.mProtocolVersion != null) {
-            return mAccount.mProtocolVersion;
-        }
-        return Eas.DEFAULT_PROTOCOL_VERSION;
+        return getProtocolVersion(mAccount);
     }
 
-    /**
-     * Set standard HTTP headers, using a policy key if required
-     * @param method the method we are going to send
-     * @param usePolicyKey whether or not a policy key should be sent in the headers
-     */
-    private void setHeaders(final HttpRequestBase method, final boolean usePolicyKey) {
-        method.setHeader("Authorization", makeAuthString());
-        method.setHeader("MS-ASProtocolVersion", getProtocolVersion());
-        method.setHeader("User-Agent", USER_AGENT);
-        method.setHeader("Accept-Encoding", "gzip");
-        if (usePolicyKey) {
-            // If there's an account in existence, use its key; otherwise (we're creating the
-            // account), send "0".  The server will respond with code 449 if there are policies
-            // to be enforced
-            String key = "0";
-            if (mAccount != null) {
-                final String accountKey = mAccount.mSecuritySyncKey;
-                if (!TextUtils.isEmpty(accountKey)) {
-                    key = accountKey;
-                }
-            }
-            method.setHeader("X-MS-PolicyKey", key);
-        }
-    }
-
-    protected EasResponse sendHttpClientPost(String cmd, final HttpEntity entity,
+    protected EasResponse sendHttpClientPost(final String cmd, final HttpEntity entity,
             final long timeout) throws IOException {
-        final HttpClient client = getHttpClient(timeout);
-        final boolean isPingCommand = cmd.equals("Ping");
-
-        // Split the mail sending commands
-        String extra = null;
-        boolean msg = false;
-        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
-            final int cmdLength = cmd.indexOf('&');
-            extra = cmd.substring(cmdLength);
-            cmd = cmd.substring(0, cmdLength);
-            msg = true;
-        } else if (cmd.startsWith("SendMail&")) {
-            msg = true;
-        }
-
-        final String us = makeUriString(cmd, extra);
-        final HttpPost method = new HttpPost(URI.create(us));
-        // Send the proper Content-Type header; it's always wbxml except for messages when
-        // the EAS protocol version is < 14.0
-        // If entity is null (e.g. for attachments), don't set this header
-        final String protocolVersion = getProtocolVersion();
-        final Double protocolVersionDouble = Eas.getProtocolVersionDouble(protocolVersion);
-        if (msg && (protocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) {
-            method.setHeader("Content-Type", "message/rfc822");
-        } else if (entity != null) {
-            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
-        }
-        setHeaders(method, !isPingCommand);
-        // NOTE
-        // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate
-        // network activity related to the Ping command on some networks with some servers.
-        // This code should be removed when the underlying issue is resolved
-        if (isPingCommand) {
-            method.setHeader("Connection", "close");
-        }
-        method.setEntity(entity);
-        return executePostWithTimeout(client, method);
+        return sendHttpClientPost(mAccount, mHostAuth, cmd, entity, timeout);
     }
 
     protected EasResponse sendHttpClientPost(final String cmd, final byte[] bytes)
             throws IOException {
-        return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT);
-    }
-
-    private EasResponse executePostWithTimeout(final HttpClient client, final HttpPost method)
-            throws IOException {
-        synchronized (this) {
-            if (mStopped) {
-                mStopped = false;
-                // If this gets stopped after the post actually starts, it throws an IOException.
-                // Therefore if we get stopped here, let's throw the same sort of exception, so
-                // callers can just equate IOException with the "this POST got killed for some
-                // reason".
-                throw new IOException("Sync was stopped before POST");
-            }
-           mPendingPost = method;
-        }
-        try {
-            // TODO: The first argument below is probably bad.
-            return EasResponse.fromHttpRequest(getClientConnectionManager(), client, method);
-        } finally {
-            synchronized (this) {
-                mPendingPost = null;
-            }
-        }
+        return sendHttpClientPost(mAccount, mHostAuth, cmd, bytes);
     }
 
     // Communication with the application.
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index e52e5b0..4b5cd51 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -161,9 +161,7 @@
                     // ping or sync might start first. It only works for startSync because sync is
                     // higher priority than ping (i.e. a ping can't start while a sync is pending)
                     // and only one ping can run at a time.
-                    EasPingSyncHandler pingHandler = new EasPingSyncHandler(context,
-                            context.getContentResolver(), account, null, null, this);
-                    pingHandler.performSync();
+                    EasPingSyncHandler pingHandler = new EasPingSyncHandler(context, account, this);
                     // TODO: error handling for pings that never took flight?
                     mPingHandlers.put(accountId, pingHandler);
                     // Whenever we have a running ping, make sure this service stays running.