From 77b653fa1b5baabddb76802f0333c78f80c29ace Mon Sep 17 00:00:00 2001 From: Yu Ping Hu Date: Mon, 7 Oct 2013 16:01:30 -0700 Subject: Improve sync logic. - Always request FolderSync when the account hasn't synced yet. - Change the account broadcast receiver to only initiate FolderSync for accounts with all syncs disabled. - Add a mailbox type filter to ONLY sync calendar/contacts. Type filtered sync does not trigger FolderSync. - Do not sync non-enabled types when syncing multiple folders (i.e. for account sync, or for type sync). Bug: 11081520 Change-Id: Ic98e43a9fa3ed46d0597c348a5823d1accab7045 --- .../exchange/ExchangeBroadcastReceiver.java | 26 +++-- .../service/CalendarSyncAdapterService.java | 64 +++-------- .../service/ContactsSyncAdapterService.java | 63 +++------- .../exchange/service/EmailSyncAdapterService.java | 128 +++++++++++++-------- 4 files changed, 124 insertions(+), 157 deletions(-) diff --git a/src/com/android/exchange/ExchangeBroadcastReceiver.java b/src/com/android/exchange/ExchangeBroadcastReceiver.java index f1918489..fbc424d1 100644 --- a/src/com/android/exchange/ExchangeBroadcastReceiver.java +++ b/src/com/android/exchange/ExchangeBroadcastReceiver.java @@ -9,6 +9,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.provider.CalendarContract; +import android.provider.ContactsContract; import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.Mailbox; @@ -18,17 +20,23 @@ import com.android.mail.utils.LogUtils; public class ExchangeBroadcastReceiver extends BroadcastReceiver { @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(final Context context, final Intent intent) { final Account[] accounts = AccountManager.get(context) .getAccountsByType(context.getString(string.account_manager_type_exchange)); - LogUtils.i(Eas.LOG_TAG, "Accounts changed - requesting account sync for all accounts"); - for (Account account : accounts) { - final Bundle bundle = new Bundle(); - bundle.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); - bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); - bundle.putLong( - Mailbox.SYNC_EXTRA_MAILBOX_ID, Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY); - ContentResolver.requestSync(account, EmailContent.AUTHORITY, bundle); + LogUtils.i(Eas.LOG_TAG, "Accounts changed - requesting FolderSync for unsynced accounts"); + for (final Account account : accounts) { + // Only do a sync for accounts that are not configured to sync any types, since the + // initial sync will do the right thing if at least one of those is enabled. + if (!ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY) && + !ContentResolver.getSyncAutomatically(account, CalendarContract.AUTHORITY) && + !ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY)) { + final Bundle bundle = new Bundle(3); + bundle.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); + bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + bundle.putLong( + Mailbox.SYNC_EXTRA_MAILBOX_ID, Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY); + ContentResolver.requestSync(account, EmailContent.AUTHORITY, bundle); + } } } } diff --git a/src/com/android/exchange/service/CalendarSyncAdapterService.java b/src/com/android/exchange/service/CalendarSyncAdapterService.java index c8f00cf1..c3683c7f 100644 --- a/src/com/android/exchange/service/CalendarSyncAdapterService.java +++ b/src/com/android/exchange/service/CalendarSyncAdapterService.java @@ -27,7 +27,6 @@ import android.os.Bundle; import android.provider.CalendarContract.Events; import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.Mailbox; import com.android.exchange.Eas; @@ -101,58 +100,23 @@ public class CalendarSyncAdapterService extends AbstractSyncAdapterService { } } + // Forward the sync request to the EmailSyncAdapterService. + final Bundle mailExtras = new Bundle(4); + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); + if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + } final long extrasMailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, 0); - final boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); if (extrasMailboxId != 0) { - // If we've been given a mailbox, just sync that one and be done. Don't - // do a query for the rest of the calendar mailboxes. - syncMailbox(account, expedited, extrasMailboxId); - return; - } - - // Find the (EmailProvider) account associated with this email address - final Cursor accountCursor = - cr.query(com.android.emailcommon.provider.Account.CONTENT_URI, - EmailContent.ID_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?", - new String[] {account.name}, null); - if (accountCursor == null) { - LogUtils.e(TAG, "Null account cursor in CalendarSyncAdapterService"); - return; - } - - try { - if (accountCursor.moveToFirst()) { - final long accountId = accountCursor.getLong(0); - // Now, find the calendar mailbox associated with the account - final Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, Mailbox.ID_PROJECTION, - ACCOUNT_AND_TYPE_CALENDAR, new String[] {Long.toString(accountId)}, null); - try { - while (mailboxCursor.moveToNext()) { - // TODO: Currently just bouncing this to Email sync; eventually streamline. - final long mailboxId = mailboxCursor.getLong(Mailbox.ID_PROJECTION_COLUMN); - syncMailbox(account, expedited, mailboxId); - } - } finally { - mailboxCursor.close(); - } - } - } finally { - accountCursor.close(); - } - } - - private static void syncMailbox(Account account, boolean expedited, long mailboxId) { - // TODO: Should we be using the existing extras and just adding our bits? - final Bundle mailboxExtras = new Bundle(4); - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); - if (expedited) { - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + // If we've been given a mailbox, specify a sync for just that mailbox. + mailExtras.putLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, extrasMailboxId); + } else { + // Otherwise, specify a sync for all calendars. + mailExtras.putInt(Mailbox.SYNC_EXTRA_MAILBOX_TYPE, Mailbox.TYPE_CALENDAR); } - mailboxExtras.putLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, mailboxId); - ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailboxExtras); + ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailExtras); LogUtils.i(TAG, "requestSync CalendarSyncAdapter %s, %s", - account.toString(), mailboxExtras.toString()); - + account.toString(), mailExtras.toString()); } } diff --git a/src/com/android/exchange/service/ContactsSyncAdapterService.java b/src/com/android/exchange/service/ContactsSyncAdapterService.java index 19986fb6..8fae508a 100644 --- a/src/com/android/exchange/service/ContactsSyncAdapterService.java +++ b/src/com/android/exchange/service/ContactsSyncAdapterService.java @@ -29,7 +29,6 @@ import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.RawContacts; import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.Mailbox; import com.android.exchange.Eas; @@ -112,59 +111,23 @@ public class ContactsSyncAdapterService extends AbstractSyncAdapterService { } } - // Find the (EmailProvider) account associated with this email address - final Cursor accountCursor = - cr.query(com.android.emailcommon.provider.Account.CONTENT_URI, - com.android.emailcommon.provider.Account.ID_PROJECTION, - AccountColumns.EMAIL_ADDRESS + "=?", - new String[] {account.name}, null); - if (accountCursor == null) { - LogUtils.e(TAG, "null account cursor in ContactsSyncAdapterService"); - return; + // Forward the sync request to the EmailSyncAdapterService. + final Bundle mailExtras = new Bundle(4); + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); + if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { + mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); } - final long extrasMailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, 0); - final boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); if (extrasMailboxId != 0) { - // If we've been given a mailbox, just sync that one and be done. Don't - // do a query for the rest of the calendar mailboxes. - syncMailbox(account, expedited, extrasMailboxId); - return; - } - - try { - if (accountCursor.moveToFirst()) { - final long accountId = accountCursor.getLong( - com.android.emailcommon.provider.Account.ID_PROJECTION_COLUMN); - // Now, find the contacts mailbox associated with the account - final Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, Mailbox.ID_PROJECTION, - ACCOUNT_AND_TYPE_CONTACTS, new String[] {Long.toString(accountId)}, null); - try { - while (mailboxCursor.moveToNext()) { - // TODO: Currently just bouncing this to Email sync; eventually streamline. - final long mailboxId = mailboxCursor.getLong(Mailbox.ID_PROJECTION_COLUMN); - syncMailbox(account, expedited, mailboxId); - } - } finally { - mailboxCursor.close(); - } - } - } finally { - accountCursor.close(); - } - } - - private static void syncMailbox(Account account, boolean expedited, long mailboxId) { - // TODO: Should we be using the existing extras and just adding our bits? - final Bundle mailboxExtras = new Bundle(4); - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); - if (expedited) { - mailboxExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + // If we've been given a mailbox, specify a sync for just that mailbox. + mailExtras.putLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, extrasMailboxId); + } else { + // Otherwise, specify a sync for all calendars. + mailExtras.putInt(Mailbox.SYNC_EXTRA_MAILBOX_TYPE, Mailbox.TYPE_CONTACTS); } - mailboxExtras.putLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, mailboxId); - ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailboxExtras); + ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailExtras); LogUtils.i(TAG, "requestSync ContactsSyncAdapter %s, %s", - account.toString(), mailboxExtras.toString()); + account.toString(), mailExtras.toString()); } } diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java index ba5633a0..2261c0fb 100644 --- a/src/com/android/exchange/service/EmailSyncAdapterService.java +++ b/src/com/android/exchange/service/EmailSyncAdapterService.java @@ -208,17 +208,7 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { account.mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE); boolean pushNeeded = false; if (account.mSyncInterval == Account.CHECK_INTERVAL_PUSH) { - // Determine which content types want sync. - final HashSet authsToSync = new HashSet(); - if (ContentResolver.getSyncAutomatically(amAccount, EmailContent.AUTHORITY)) { - authsToSync.add(EmailContent.AUTHORITY); - } - if (ContentResolver.getSyncAutomatically(amAccount, CalendarContract.AUTHORITY)) { - authsToSync.add(CalendarContract.AUTHORITY); - } - if (ContentResolver.getSyncAutomatically(amAccount, ContactsContract.AUTHORITY)) { - authsToSync.add(ContactsContract.AUTHORITY); - } + final HashSet authsToSync = getAuthsToSync(amAccount); // If we have at least one sync-enabled content type, check for syncing mailboxes. if (!authsToSync.isEmpty()) { final Cursor c = Mailbox.getMailboxesForPush(service.getContentResolver(), @@ -632,16 +622,23 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { } finally { accountCursor.close(); } - // Get the mailbox that we want to sync. - // There are four possibilities for Mailbox.SYNC_EXTRA_MAILBOX_ID: - // 1) Mailbox.SYNC_EXTRA_MAILBOX_ID_PUSH_ONLY: Restart push if appropriate. - // 2) Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY: Sync only the account data. - // 3) Not present: Perform a full account sync. - // 4) Non-negative value: It's an actual mailbox id, sync that mailbox only. - final long mailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, FULL_ACCOUNT_SYNC); - - // If we're just twiddling the push, we do the lightweight thing and just bail. - if (mailboxId == Mailbox.SYNC_EXTRA_MAILBOX_ID_PUSH_ONLY) { + + // Figure out what we want to sync, based on the extras and our account sync status. + final boolean isInitialSync = EmailContent.isInitialSyncKey(account.mSyncKey); + final long mailboxId = extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, + Mailbox.NO_MAILBOX); + final int mailboxType = extras.getInt(Mailbox.SYNC_EXTRA_MAILBOX_TYPE, + Mailbox.TYPE_NONE); + + // A "full sync" means no specific mailbox or type filter was requested. + final boolean isFullSync = (mailboxId == Mailbox.NO_MAILBOX && + mailboxType == Mailbox.TYPE_NONE); + // A FolderSync is necessary for full sync, initial sync, and account only sync. + final boolean isFolderSync = (isFullSync || isInitialSync || + mailboxId == Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY); + + // If we're just twiddling the push, we do the lightweight thing and bail early. + if (mailboxId == Mailbox.SYNC_EXTRA_MAILBOX_ID_PUSH_ONLY && !isFolderSync) { mSyncHandlerMap.modifyPing(account); LogUtils.i(TAG, "onPerformSync: mailbox push only %s, %s", acct.toString(), extras.toString()); @@ -651,40 +648,50 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { // Do the bookkeeping for starting a sync, including stopping a ping if necessary. mSyncHandlerMap.startSync(account.mId); + // Perform a FolderSync if necessary. + if (isFolderSync) { + final EasFolderSync folderSync = new EasFolderSync(context, account); + folderSync.doFolderSync(syncResult); + } + // Perform email upsync for this account. Moves first, then state changes. - EasMoveItems move = new EasMoveItems(context, account); - move.upsyncMovedMessages(syncResult); - // TODO: EasSync should eventually handle both up and down; for now, it's used purely - // for upsync. - EasSync upsync = new EasSync(context, account); - upsync.upsync(syncResult); - - // TODO: Should we refresh the account here? It may have changed while waiting for any + if (!isInitialSync) { + EasMoveItems move = new EasMoveItems(context, account); + move.upsyncMovedMessages(syncResult); + // TODO: EasSync should eventually handle both up and down; for now, it's used + // purely for upsync. + EasSync upsync = new EasSync(context, account); + upsync.upsync(syncResult); + } + + // TODO: Should we refresh account here? It may have changed while waiting for any // pings to stop. It may not matter since the things that may have been twiddled might // not affect syncing. - if (mailboxId == FULL_ACCOUNT_SYNC || - mailboxId == Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY) { - final EasFolderSync folderSync = new EasFolderSync(context, account); - folderSync.doFolderSync(syncResult); - - if (mailboxId == FULL_ACCOUNT_SYNC) { + if (mailboxId != Mailbox.NO_MAILBOX) { + // Sync the mailbox that was explicitly requested. + syncMailbox(context, cr, acct, account, mailboxId, extras, syncResult, null, true); + } else if (mailboxId != Mailbox.SYNC_EXTRA_MAILBOX_ID_ACCOUNT_ONLY) { + // We have to sync multiple folders. + final Cursor c; + if (isFullSync) { // 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, false); - } - } finally { - c.close(); + c = Mailbox.getMailboxIdsForSync(cr, account.mId); + } else { + // Type-filtered sync should only get the mailboxes of a specific type. + c = Mailbox.getMailboxIdsForSyncByType(cr, account.mId, mailboxType); + } + if (c != null) { + try { + final HashSet authsToSync = getAuthsToSync(acct); + while (c.moveToNext()) { + syncMailbox(context, cr, acct, account, c.getLong(0), extras, + syncResult, authsToSync, false); } + } finally { + c.close(); } } - } else { - // Sync the mailbox that was explicitly requested. - syncMailbox(context, cr, acct, account, mailboxId, extras, syncResult, true); } // Clean up the bookkeeping, including restarting ping if necessary. @@ -715,7 +722,8 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { 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 boolean isMailboxSync) { + final Bundle extras, final SyncResult syncResult, final HashSet authsToSync, + final boolean isMailboxSync) { final Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId); if (mailbox == null) { return false; @@ -726,6 +734,11 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { extras.toString()); return false; } + if (authsToSync != null && !authsToSync.contains(Mailbox.getAuthority(mailbox.mType))) { + // We are asking for an account sync, but this mailbox type is not configured for + // sync. + return false; + } final boolean success; // Non-mailbox syncs are whole account syncs initiated by the AccountManager and are @@ -791,4 +804,23 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { IntentUtilities.setAccountName(builder, accountName); return new Intent(Intent.ACTION_EDIT, builder.build()); } + + /** + * Determine which content types are set to sync for an account. + * @param account The account whose sync settings we're looking for. + * @return The authorities for the content types we want to sync for account. + */ + private static HashSet getAuthsToSync(final android.accounts.Account account) { + final HashSet authsToSync = new HashSet(); + if (ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY)) { + authsToSync.add(EmailContent.AUTHORITY); + } + if (ContentResolver.getSyncAutomatically(account, CalendarContract.AUTHORITY)) { + authsToSync.add(CalendarContract.AUTHORITY); + } + if (ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY)) { + authsToSync.add(ContactsContract.AUTHORITY); + } + return authsToSync; + } } -- cgit v1.2.3