Fix account sync.

We no longer specify sync intervals per mailbox. Instead,
the account sync interval is always used, and each mailbox
simply says whether it participates in that sync. This
allows us to change "account sync" to mean that we sync all
mailboxes that we are actively syncing (and push uses the
same definition of which mailboxes to check).

Also, this change implements "account only" sync.

Change-Id: I528ef5f9f0b857fd2a31a95068ef1b5ff62a5a56
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
index ca2a8a5..74a1f80 100644
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ b/src/com/android/exchange/adapter/AbstractSyncParser.java
@@ -139,10 +139,7 @@
                     if (status == 3 || CommandStatus.isBadSyncKey(status)) {
                         // Must delete all of the data and start over with syncKey of "0"
                         mMailbox.mSyncKey = "0";
-                        // Make this a push box through the first sync
-                        // TODO Make frequency conditional on user settings!
-                        mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
-                        // TODO: Implement wipe if needed.
+                        // TODO: Implement wipe.
                         //mAdapter.wipe();
                         // Indicate there's more so that we'll start syncing again
                         moreAvailable = true;
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 08dc148..cd5f2d1 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -406,46 +406,46 @@
         cv.put(MailboxColumns.PARENT_SERVER_ID, parentId);
         cv.put(MailboxColumns.ACCOUNT_KEY, mAccountId);
         final int mailboxType;
-        final long syncInterval;
+        final boolean shouldSync;
         switch (easType) {
             case INBOX_TYPE:
                 mailboxType = Mailbox.TYPE_INBOX;
-                syncInterval = mAccount.mSyncInterval;
+                shouldSync = true;
                 break;
             case CONTACTS_TYPE:
                 mailboxType = Mailbox.TYPE_CONTACTS;
-                syncInterval = mAccount.mSyncInterval;
+                shouldSync = true;
                 break;
             case OUTBOX_TYPE:
                 // TYPE_OUTBOX mailboxes are known by ExchangeService to sync whenever they
                 // aren't empty.  The value of mSyncFrequency is ignored for this kind of
                 // mailbox.
                 mailboxType = Mailbox.TYPE_OUTBOX;
-                syncInterval = Mailbox.CHECK_INTERVAL_NEVER;
+                shouldSync = false;
                 mOutboxCreated = true;
                 break;
             case SENT_TYPE:
                 mailboxType = Mailbox.TYPE_SENT;
-                syncInterval = Mailbox.CHECK_INTERVAL_NEVER;
+                shouldSync = false;
                 break;
             case DRAFTS_TYPE:
                 mailboxType = Mailbox.TYPE_DRAFTS;
-                syncInterval = Mailbox.CHECK_INTERVAL_NEVER;
+                shouldSync = false;
                 break;
             case DELETED_TYPE:
                 mailboxType = Mailbox.TYPE_TRASH;
-                syncInterval = Mailbox.CHECK_INTERVAL_NEVER;
+                shouldSync = false;
                 break;
             case CALENDAR_TYPE:
                 mailboxType = Mailbox.TYPE_CALENDAR;
-                syncInterval = mAccount.mSyncInterval;
+                shouldSync = true;
                 break;
             default:
                 mailboxType = Mailbox.TYPE_MAIL;
-                syncInterval = Mailbox.CHECK_INTERVAL_NEVER;
+                shouldSync = false;
         }
         cv.put(MailboxColumns.TYPE, mailboxType);
-        cv.put(MailboxColumns.SYNC_INTERVAL, syncInterval);
+        cv.put(MailboxColumns.SYNC_INTERVAL, shouldSync ? 1 : 0);
 
         // Set basic flags
         int flags = 0;
diff --git a/src/com/android/exchange/service/EasPingSyncHandler.java b/src/com/android/exchange/service/EasPingSyncHandler.java
index 5144df7..d0d8d4d 100644
--- a/src/com/android/exchange/service/EasPingSyncHandler.java
+++ b/src/com/android/exchange/service/EasPingSyncHandler.java
@@ -36,8 +36,6 @@
     private final PingTask mPingTask;
 
     private class PingTask extends AsyncTask<Void, Void, Void> {
-        private static final String AND_FREQUENCY_PUSH = " AND " + MailboxColumns.SYNC_INTERVAL +
-                '=' + Mailbox.CHECK_INTERVAL_PUSH;
         private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
                 MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
 
@@ -58,9 +56,8 @@
             // prevents us from proceeding.
             boolean continuePing = true;
             while(continuePing) {
-                final Cursor c = mContentResolver.query(Mailbox.CONTENT_URI,
-                        Mailbox.CONTENT_PROJECTION, MailboxColumns.ACCOUNT_KEY + '=' +
-                        mAccount.mId + AND_FREQUENCY_PUSH, null, null);
+                final Cursor c =
+                        Mailbox.getMailboxesForPush(mContentResolver, mAccount.mId);
                 if (c == null) {
                     // TODO: Signal error: can't read mailbox data.
                     break;
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index e752162..4caf7fa 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -58,6 +58,11 @@
     private static final String TAG = "EAS EmailSyncAdaptSvc";
 
     /**
+     * If sync extras do not include a mailbox id, then we want to perform a full sync.
+     */
+    private static final long FULL_ACCOUNT_SYNC = Mailbox.NO_MAILBOX;
+
+    /**
      * Bookkeeping for handling synchronization between pings and syncs.
      * "Ping" refers to a hanging POST or GET that is used to receive push notifications. Ping is
      * the term for the Exchange command, but this code should be generic enough to be easily
@@ -185,7 +190,8 @@
                     // 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, account, this);
+                    final EasPingSyncHandler pingHandler =
+                            new EasPingSyncHandler(context, account, this);
                     mPingHandlers.put(accountId, pingHandler);
                     // Whenever we have a running ping, make sure this service stays running.
                     final EmailSyncAdapterService service = EmailSyncAdapterService.this;
@@ -436,16 +442,16 @@
         }
 
         @Override
-        public void onPerformSync(android.accounts.Account acct, Bundle extras,
-                String authority, ContentProviderClient provider, SyncResult syncResult) {
-
+        public void onPerformSync(final android.accounts.Account acct, final Bundle extras,
+                final String authority, final ContentProviderClient provider,
+                final SyncResult syncResult) {
+            LogUtils.i(TAG, "performSync: extras = %s", extras.toString());
             TempDirectory.setTempDirectory(EmailSyncAdapterService.this);
 
             // TODO: Perform any connectivity checks, bail early if we don't have proper network
             // for this sync operation.
 
             final Context context = getContext();
-            LogUtils.i(TAG, "performSync");
             final ContentResolver cr = context.getContentResolver();
 
             // Get the EmailContent Account
@@ -464,9 +470,6 @@
                 accountCursor.close();
             }
 
-            // TODO: If this account is on security hold (i.e. not enforcing policy), do not permit
-            // sync to occur.
-
             // Do the bookkeeping for starting a sync, including stopping a ping if necessary.
             mSyncHandlerMap.startSync(account.mId);
 
@@ -474,46 +477,39 @@
             // pings to stop. It may not matter since the things that may have been twiddled might
             // not affect syncing.
 
-            final long mailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID,
-                    Mailbox.NO_MAILBOX);
-            if (mailboxId == Mailbox.NO_MAILBOX) {
-                // If no mailbox is specified, this is an account sync.
+            // There are three possibilities for Mailbox.SYNC_EXTRA_MAILBOX_ID:
+            // 1) It's Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY. Sync only the account data.
+            // 2) It's not present. Perform a full account sync.
+            // 3) It's a mailbox id (non-negative value). Sync that mailbox only.
+            final long mailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, FULL_ACCOUNT_SYNC);
+            if (mailboxId == FULL_ACCOUNT_SYNC ||
+                    mailboxId == Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY) {
                 final EasAccountSyncHandler accountSyncHandler =
                         new EasAccountSyncHandler(context, account);
                 accountSyncHandler.performSync();
 
-                // Account sync also does an inbox sync.
-                final Mailbox inbox = Mailbox.restoreMailboxOfType(context, account.mId,
-                        Mailbox.TYPE_INBOX);
-                final EasSyncHandler inboxSyncHandler = EasSyncHandler.getEasSyncHandler(
-                        context, cr, acct, account, inbox, extras, syncResult);
-                if (inboxSyncHandler == null) {
-                    // TODO: Inbox does not exist for this account, add proper error handling.
-                } else {
-                    inboxSyncHandler.performSync();
+                if (mailboxId == Mailbox.NO_MAILBOX) {
+                    // Full account sync includes all mailboxes that participate in system sync.
+                    final Cursor c = Mailbox.getMailboxIdsForSync(cr, account.mId);
+                    if (c != null) {
+                        try {
+                            while (c.moveToNext()) {
+                                syncMailbox(context, cr, acct, account, c.getLong(0), extras,
+                                        syncResult);
+                            }
+                        } finally {
+                            c.close();
+                        }
+                    }
                 }
-
-                // TODO: Do an outbox sync as well?
             } else {
                 // Sync the mailbox that was explicitly requested.
-                final Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
-                if (mailbox != null && mailbox.mType == Mailbox.TYPE_OUTBOX) {
-                    final EasOutboxSyncHandler outboxSyncHandler =
-                            new EasOutboxSyncHandler(context, account, mailbox);
-                    outboxSyncHandler.performSync();
-                } else {
-                    final EasSyncHandler syncHandler = EasSyncHandler.getEasSyncHandler(context, cr,
-                            acct, account, mailbox, extras, syncResult);
-
-                    if (syncHandler != null) {
-                        syncHandler.performSync();
-                    } else {
-                        // We can't sync this mailbox, so just send the expected UI callbacks.
-                        EmailServiceStatus.syncMailboxStatus(cr, extras, mailboxId,
-                                EmailServiceStatus.IN_PROGRESS, 0);
-                        EmailServiceStatus.syncMailboxStatus(cr, extras, mailboxId,
-                                EmailServiceStatus.SUCCESS, 0);
-                    }
+                if (!syncMailbox(context, cr, acct, account, mailboxId, extras, syncResult)) {
+                    // We can't sync this mailbox, so just send the expected UI callbacks.
+                    EmailServiceStatus.syncMailboxStatus(cr, extras, mailboxId,
+                            EmailServiceStatus.IN_PROGRESS, 0);
+                    EmailServiceStatus.syncMailboxStatus(cr, extras, mailboxId,
+                            EmailServiceStatus.SUCCESS, 0);
                 }
             }
 
@@ -524,5 +520,28 @@
             // 1) performSync return value can signal some useful info.
             // 2) syncResult can contain useful info.
         }
+
+        private boolean syncMailbox(final Context context, final ContentResolver cr,
+                final android.accounts.Account acct, final Account account, final long mailboxId,
+                final Bundle extras, final SyncResult syncResult) {
+            final Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
+            if (mailbox == null) {
+                return false;
+            }
+
+            if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
+                final EasOutboxSyncHandler outboxSyncHandler =
+                        new EasOutboxSyncHandler(context, account, mailbox);
+                outboxSyncHandler.performSync();
+            } else {
+                final EasSyncHandler syncHandler = EasSyncHandler.getEasSyncHandler(context, cr,
+                        acct, account, mailbox, extras, syncResult);
+                if (syncHandler == null) {
+                    return false;
+                }
+                syncHandler.performSync();
+            }
+            return true;
+        }
     }
 }