Fill out implemenation of PingSyncSynchronizer. This class is not
enabled yet as it is utilized by EasService.

Change-Id: Iba3faef51df89b7fd103f43a33d7a401466a344c

Conflicts:
	src/com/android/exchange/service/EasService.java
	src/com/android/exchange/service/PingSyncSynchronizer.java
	src/com/android/exchange/service/PingTask.java
diff --git a/src/com/android/exchange/eas/EasOperation.java b/src/com/android/exchange/eas/EasOperation.java
index 07fa1f9..9b0ab35 100644
--- a/src/com/android/exchange/eas/EasOperation.java
+++ b/src/com/android/exchange/eas/EasOperation.java
@@ -222,6 +222,10 @@
         return mAccountId;
     }
 
+    public final Account getAccount() {
+        return mAccount;
+    }
+
     /**
      * Request that this operation terminate. Intended for use by the sync service to interrupt
      * running operations, primarily Ping.
diff --git a/src/com/android/exchange/service/EasService.java b/src/com/android/exchange/service/EasService.java
index 41aa48c..b7c366d 100644
--- a/src/com/android/exchange/service/EasService.java
+++ b/src/com/android/exchange/service/EasService.java
@@ -96,7 +96,7 @@
             LogUtils.d(TAG, "IEmailService.pushModify: %d", accountId);
             final Account account = Account.restoreAccountWithId(EasService.this, accountId);
             if (pingNeededForAccount(account)) {
-                mSynchronizer.pushModify(accountId);
+                mSynchronizer.pushModify(account);
             } else {
                 mSynchronizer.pushStop(accountId);
             }
@@ -162,7 +162,7 @@
                         account.restore(c);
                         if (EasService.this.pingNeededForAccount(account)) {
                             mHasRestartedPing = true;
-                            EasService.this.mSynchronizer.pushModify(account.mId);
+                            EasService.this.mSynchronizer.pushModify(account);
                         }
                     }
                 } finally {
@@ -232,6 +232,7 @@
 
     public int doOperation(final EasOperation operation, final String loggingName) {
         final long accountId = operation.getAccountId();
+        final Account account = operation.getAccount();
         LogUtils.d(TAG, "%s: %d", loggingName, accountId);
         mSynchronizer.syncStart(accountId);
         // TODO: Do we need a wakelock here? For RPC coming from sync adapters, no -- the SA
@@ -242,7 +243,7 @@
         try {
             return operation.performOperation();
         } finally {
-            mSynchronizer.syncEnd(accountId);
+            mSynchronizer.syncEnd(account);
         }
     }
 
diff --git a/src/com/android/exchange/service/PingSyncSynchronizer.java b/src/com/android/exchange/service/PingSyncSynchronizer.java
index 7871f0f..f357881 100644
--- a/src/com/android/exchange/service/PingSyncSynchronizer.java
+++ b/src/com/android/exchange/service/PingSyncSynchronizer.java
@@ -17,10 +17,13 @@
 package com.android.exchange.service;
 
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.support.v4.util.LongSparseArray;
 
+import com.android.emailcommon.provider.Account;
 import com.android.exchange.Eas;
+import com.android.exchange.eas.EasPing;
 import com.android.mail.utils.LogUtils;
 
 import java.util.concurrent.locks.Condition;
@@ -106,6 +109,20 @@
         }
 
         /**
+         * Helper function that starts a ping task
+         * @param account The {@link Account} in question.
+         * @param synchronizer Parent {@link PingSyncSynchronizer} object.
+         */
+        private void startPingTask(final Account account, final PingSyncSynchronizer synchronizer) {
+            final android.accounts.Account amAccount =
+                    new android.accounts.Account(account.mEmailAddress,
+                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
+            mPingTask = new PingTask(synchronizer.getContext(), account, amAccount,
+                    synchronizer);
+            mPingTask.start();
+        }
+
+        /**
          * Update bookkeeping for a new sync:
          * - Stop the Ping if there is one.
          * - Wait until there's nothing running for this account before proceeding.
@@ -134,7 +151,7 @@
          * go ahead, or starting the ping if appropriate and there are no waiting ops.
          * @return Whether this account is now idle.
          */
-        public boolean syncEnd() {
+        public boolean syncEnd(final Account account, final PingSyncSynchronizer synchronizer) {
             --mSyncCount;
             if (mSyncCount > 0) {
                 LogUtils.d(TAG, "Signalling a pending sync to proceed.");
@@ -142,7 +159,7 @@
                 return false;
             } else {
                 if (mPushEnabled) {
-                    // TODO: Start the ping task
+                    startPingTask(account, synchronizer);
                     return false;
                 }
             }
@@ -153,14 +170,19 @@
          * Update bookkeeping when the ping task terminates, including signaling any waiting ops.
          * @return Whether this account is now idle.
          */
-        public boolean pingEnd() {
+        public boolean pingEnd(final android.accounts.Account amAccount) {
             mPingTask = null;
             if (mSyncCount > 0) {
                 mCondition.signal();
                 return false;
             } else {
                 if (mPushEnabled) {
-                    // TODO: request a push-only sync.
+                    /**
+                     * This situation only arises if we encountered some sort of error that
+                     * stopped our ping but not due to a sync interruption. In this scenario
+                     * we'll leverage the SyncManager to request a push only sync that will
+                     * restart the ping when the time is right. */
+                    EasPing.requestPing(amAccount);
                     return false;
                 }
             }
@@ -170,14 +192,12 @@
         /**
          * Modifies or starts a ping for this account if no syncs are running.
          */
-        public void pushModify() {
+        public void pushModify(final Account account, final PingSyncSynchronizer synchronizer) {
             mPushEnabled = true;
             if (mSyncCount == 0) {
                 if (mPingTask == null) {
                     // No ping, no running syncs -- start a new ping.
-                    // TODO: Fix this.
-                    //mPingTask = new PingTask();
-                    //mPingTask.start();
+                    startPingTask(account, synchronizer);
                 } else {
                     // Ping is already running, so tell it to restart to pick up any new params.
                     mPingTask.restart();
@@ -220,6 +240,10 @@
         mService = service;
     }
 
+    public Context getContext() {
+        return mService;
+    }
+
     /**
      * Gets the {@link AccountSyncState} for an account.
      * The caller must hold {@link #mLock}.
@@ -270,16 +294,17 @@
         }
     }
 
-    public void syncEnd(final long accountId) {
+    public void syncEnd(final Account account) {
         mLock.lock();
         try {
+            final long accountId = account.getId();
             LogUtils.d(TAG, "PSS syncEnd for account %d", accountId);
             final AccountSyncState accountState = getAccountState(accountId, false);
             if (accountState == null) {
                 LogUtils.w(TAG, "PSS syncEnd for account %d but no state found", accountId);
                 return;
             }
-            if (accountState.syncEnd()) {
+            if (accountState.syncEnd(account, this)) {
                 removeAccount(accountId);
             }
         } finally {
@@ -287,7 +312,7 @@
         }
     }
 
-    public void pingEnd(final long accountId) {
+    public void pingEnd(final long accountId, final android.accounts.Account amAccount) {
         mLock.lock();
         try {
             LogUtils.d(TAG, "PSS pingEnd for account %d", accountId);
@@ -296,7 +321,7 @@
                 LogUtils.w(TAG, "PSS pingEnd for account %d but no state found", accountId);
                 return;
             }
-            if (accountState.pingEnd()) {
+            if (accountState.pingEnd(amAccount)) {
                 removeAccount(accountId);
             }
         } finally {
@@ -304,12 +329,13 @@
         }
     }
 
-    public void pushModify(final long accountId) {
+    public void pushModify(final Account account) {
         mLock.lock();
         try {
+            final long accountId = account.getId();
             LogUtils.d(TAG, "PSS pushModify for account %d", accountId);
             final AccountSyncState accountState = getAccountState(accountId, true);
-            accountState.pushModify();
+            accountState.pushModify(account, this);
         } finally {
             mLock.unlock();
         }
diff --git a/src/com/android/exchange/service/PingTask.java b/src/com/android/exchange/service/PingTask.java
index 98b71f4..cad66cf 100644
--- a/src/com/android/exchange/service/PingTask.java
+++ b/src/com/android/exchange/service/PingTask.java
@@ -91,7 +91,7 @@
             mSyncHandlerMap.pingComplete(mOperation.getAmAccount(), mOperation.getAccountId(),
                     pingStatus);
         } else {
-            mPingSyncSynchronizer.pingEnd(mOperation.getAccountId());
+            mPingSyncSynchronizer.pingEnd(mOperation.getAccountId(), mOperation.getAmAccount());
         }
         return null;
     }
@@ -105,7 +105,7 @@
             mSyncHandlerMap.pingComplete(mOperation.getAmAccount(), mOperation.getAccountId(),
                     EasOperation.RESULT_REQUEST_FAILURE);
         } else {
-            mPingSyncSynchronizer.pingEnd(mOperation.getAccountId());
+            mPingSyncSynchronizer.pingEnd(mOperation.getAccountId(), mOperation.getAmAccount());
         }
     }
 }