Allow mail send even when email sync is disabled

* Rearrange the code to make it a bit easier to read/follow

Bug: 4507973
Change-Id: I6329f581fb124f1c3027e2bfb0406e9c483644a3
diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java
index 41c157d..f6c1c0b 100644
--- a/src/com/android/exchange/ExchangeService.java
+++ b/src/com/android/exchange/ExchangeService.java
@@ -524,6 +524,15 @@
     static class AccountList extends ArrayList<Account> {
         private static final long serialVersionUID = 1L;
 
+        @Override
+        public boolean add(Account account) {
+            // Cache the account manager account
+            account.mAmAccount = new android.accounts.Account(account.mEmailAddress,
+                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
+            super.add(account);
+            return true;
+        }
+
         public boolean contains(long id) {
             for (Account account : this) {
                 if (account.mId == id) {
@@ -2119,6 +2128,67 @@
         return true;
     }
 
+    /**
+     * Convenience method to determine whether Email sync is enabled for a given account
+     * @param account the Account in question
+     * @return whether Email sync is enabled
+     */
+    private boolean canSyncEmail(android.accounts.Account account) {
+        return ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY);
+    }
+
+    /**
+     * Determine whether a mailbox of a given type in a given account can be synced automatically
+     * by ExchangeService.  This is an increasingly complex determination, taking into account
+     * security policies and user settings (both within the Email application and in the Settings
+     * application)
+     *
+     * @param account the Account that the mailbox is in
+     * @param type the type of the Mailbox
+     * @return whether or not to start a sync
+     */
+    private boolean isMailboxSyncable(Account account, int type) {
+        // This 'if' statement performs checks to see whether or not a mailbox is a
+        // candidate for syncing based on policies, user settings, & other restrictions
+        if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) {
+            // Contacts/Calendar obey this setting from ContentResolver
+            if (!ContentResolver.getMasterSyncAutomatically()) {
+                return false;
+            }
+            // Get the right authority for the mailbox
+            String authority;
+            if (type == Mailbox.TYPE_CONTACTS) {
+                authority = ContactsContract.AUTHORITY;
+            } else {
+                authority = Calendar.AUTHORITY;
+                if (!mCalendarObservers.containsKey(account.mId)){
+                    // Make sure we have an observer for this Calendar, as
+                    // we need to be able to detect sync state changes, sigh
+                    registerCalendarObserver(account);
+                }
+            }
+            // See if "sync automatically" is set; if not, punt
+            if (!ContentResolver.getSyncAutomatically(account.mAmAccount, authority)) {
+                return false;
+            // See if the calendar is enabled from the Calendar app UI; if not, punt
+            } else if ((type == Mailbox.TYPE_CALENDAR) && !isCalendarEnabled(account.mId)) {
+                return false;
+            }
+        // Never automatically sync trash
+        } else if (type == Mailbox.TYPE_TRASH) {
+            return false;
+        // For non-outbox mail, we do three checks:
+        // 1) are we restricted by policy (i.e. manual sync only),
+        // 2) has the user checked the "Sync Email" box in Account Settings, and
+        // 3) does the user have the master "background data" box checked in Settings
+        } else if (type != Mailbox.TYPE_OUTBOX &&
+                (!canAutoSync(account) || !canSyncEmail(account.mAmAccount) ||
+                        !mBackgroundData)) {
+            return false;
+        }
+        return true;
+    }
+
     private long checkMailboxes () {
         // First, see if any running mailboxes have been deleted
         ArrayList<Long> deletedMailboxes = new ArrayList<Long>();
@@ -2157,80 +2227,30 @@
             log("mAccountObserver null; service died??");
             return nextWait;
         }
+
         Cursor c = getContentResolver().query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
                 mAccountObserver.getSyncableEasMailboxWhere(), null, null);
 
-        // Contacts/Calendar obey this setting from ContentResolver
-        // Mail is on its own schedule
-        boolean masterAutoSync = ContentResolver.getMasterSyncAutomatically();
         try {
             while (c.moveToNext()) {
-                long mid = c.getLong(Mailbox.CONTENT_ID_COLUMN);
+                long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
                 AbstractSyncService service = null;
                 synchronized (sSyncLock) {
-                    service = mServiceMap.get(mid);
+                    service = mServiceMap.get(mailboxId);
                 }
                 if (service == null) {
-                    // We handle a few types of mailboxes specially
-                    int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
-
-                    // If background data is off, we only sync Outbox
-                    // Manual syncs are initiated elsewhere, so they will continue to be respected
-                    if (!mBackgroundData && type != Mailbox.TYPE_OUTBOX) {
-                        continue;
-                    }
-
-                    Account account =
-                        getAccountById(c.getInt(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN));
+                    // Get the cached account
+                    Account account = getAccountById(c.getInt(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN));
                     if (account == null) continue;
 
-                    // TODO: Don't rebuild this account manager account each time through
-                    android.accounts.Account accountManagerAccount =
-                        new android.accounts.Account(account.mEmailAddress,
-                                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-
-                    if (!canAutoSync(account)) {
-                        continue;
-                    }
-
-                    if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) {
-                        // We don't sync these automatically if master auto sync is off
-                        if (!masterAutoSync) {
-                            continue;
-                        }
-                        // Get the right authority for the mailbox
-                        String authority;
-                        if (type == Mailbox.TYPE_CONTACTS) {
-                            authority = ContactsContract.AUTHORITY;
-                        } else {
-                            authority = Calendar.AUTHORITY;
-                            if (!mCalendarObservers.containsKey(account.mId)){
-                                // Make sure we have an observer for this Calendar, as
-                                // we need to be able to detect sync state changes, sigh
-                                registerCalendarObserver(account);
-                            }
-                        }
-                        // See if "sync automatically" is set; if not, punt
-                        if (!ContentResolver.getSyncAutomatically(accountManagerAccount,
-                                authority)) {
-                            continue;
-                            // See if the calendar is enabled; if not, punt
-                        } else if ((type == Mailbox.TYPE_CALENDAR) &&
-                                !isCalendarEnabled(account.mId)) {
-                            continue;
-                        }
-                    } else if (type == Mailbox.TYPE_TRASH) {
-                        // Never automatically sync trash
-                        continue;
-                    } else if (type < Mailbox.TYPE_NOT_EMAIL &&
-                            !ContentResolver.getSyncAutomatically(accountManagerAccount,
-                                    EmailContent.AUTHORITY)) {
-                        // Don't sync mail if user hasn't chosen to sync it automatically
+                    // We handle a few types of mailboxes specially
+                    int mailboxType = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
+                    if (!isMailboxSyncable(account, mailboxType)) {
                         continue;
                     }
 
                     // Check whether we're in a hold (temporary or permanent)
-                    SyncError syncError = mSyncErrorMap.get(mid);
+                    SyncError syncError = mSyncErrorMap.get(mailboxId);
                     if (syncError != null) {
                         // Nothing we can do about fatal errors
                         if (syncError.fatal) continue;
@@ -2249,23 +2269,19 @@
                     }
 
                     // Otherwise, we use the sync interval
-                    long interval = c.getInt(Mailbox.CONTENT_SYNC_INTERVAL_COLUMN);
-                    if (interval == Mailbox.CHECK_INTERVAL_PUSH) {
+                    long syncInterval = c.getInt(Mailbox.CONTENT_SYNC_INTERVAL_COLUMN);
+                    if (syncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
                         Mailbox m = EmailContent.getContent(c, Mailbox.class);
                         requestSync(m, SYNC_PUSH, null);
-                    } else if (type == Mailbox.TYPE_OUTBOX) {
+                    } else if (mailboxType == Mailbox.TYPE_OUTBOX) {
                         if (hasSendableMessages(c)) {
                             Mailbox m = EmailContent.getContent(c, Mailbox.class);
                             startServiceThread(new EasOutboxService(this, m), m);
                         }
-                    } else if (interval > 0 && interval <= ONE_DAY_MINUTES) {
+                    } else if (syncInterval > 0 && syncInterval <= ONE_DAY_MINUTES) {
                         long lastSync = c.getLong(Mailbox.CONTENT_SYNC_TIME_COLUMN);
                         long sinceLastSync = now - lastSync;
-                        if (sinceLastSync < 0) {
-                            log("WHOA! lastSync in the future for mailbox: " + mid);
-                            sinceLastSync = interval*MINUTES;
-                        }
-                        long toNextSync = interval*MINUTES - sinceLastSync;
+                        long toNextSync = syncInterval*MINUTES - sinceLastSync;
                         String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
                         if (toNextSync <= 0) {
                             Mailbox m = EmailContent.getContent(c, Mailbox.class);
@@ -2288,7 +2304,7 @@
                             log("Dead thread, mailbox released: " +
                                     c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN));
                         }
-                        releaseMailbox(mid);
+                        releaseMailbox(mailboxId);
                         // Restart this if necessary
                         if (nextWait > 3*SECONDS) {
                             nextWait = 3*SECONDS;