diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.java b/emailcommon/src/com/android/emailcommon/provider/Account.java index 8f0d093f4..7accab1b7 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Account.java +++ b/emailcommon/src/com/android/emailcommon/provider/Account.java @@ -30,6 +30,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); public static final Uri NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); + public static final Uri DEFAULT_ACCOUNT_ID_URI = + Uri.parse(EmailContent.CONTENT_URI + "/account/default"); // Define all pseudo account IDs here to avoid conflict with one another. /** @@ -545,18 +547,21 @@ public final class Account extends EmailContent implements AccountColumns, Parce /** * Return the id of the default account. If one hasn't been explicitly specified, return - * the first one in the database. For any account saved in the DB, this must be used - * to check for the default account - the mIsDefault field is set lazily and may be - * incorrect. + * the first one in the database (the logic is provided within EmailProvider) * @param context the caller's context * @return the id of the default account, or -1 if there are no accounts */ static public long getDefaultAccountId(Context context) { - long id = getDefaultAccountWhere(context, AccountColumns.IS_DEFAULT + "=1"); - if (id == -1) { - id = getDefaultAccountWhere(context, null); + Cursor c = context.getContentResolver().query( + Account.DEFAULT_ACCOUNT_ID_URI, Account.ID_PROJECTION, null, null, null); + try { + if (c != null && c.moveToFirst()) { + return c.getLong(Account.ID_PROJECTION_COLUMN); + } + } finally { + c.close(); } - return id; + return -1; } /** @@ -755,7 +760,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce // Now do the Account ContentValues cv = null; - if (recvIndex >= 0 || sendIndex >= 0) { + if (recvIndex >= 0 || sendIndex >= 0 || policyIndex >= 0) { cv = new ContentValues(); if (recvIndex >= 0) { cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex); diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java index 589a3a0d5..a3d8daae9 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java +++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java @@ -37,6 +37,8 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox"); public static final Uri ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField"); + public static final Uri FROM_ACCOUNT_AND_TYPE_URI = + Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType"); public String mDisplayName; public String mServerId; @@ -299,13 +301,27 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns } /** - * Convenience method to return the id of a given type of Mailbox for a given Account + * Convenience method to return the id of a given type of Mailbox for a given Account; the + * common Mailbox types (Inbox, Outbox, Sent, Drafts, Trash, and Search) are all cached by + * EmailProvider; therefore, we warn if the mailbox is not found in the cache + * * @param context the caller's context, used to get a ContentResolver * @param accountId the id of the account to be queried * @param type the mailbox type, as defined above * @return the id of the mailbox, or -1 if not found */ public static long findMailboxOfType(Context context, long accountId, int type) { + // First use special URI + Uri uri = FROM_ACCOUNT_AND_TYPE_URI.buildUpon().appendPath(Long.toString(accountId)) + .appendPath(Integer.toString(type)).build(); + Cursor c = context.getContentResolver().query(uri, ID_PROJECTION, null, null, null); + c.moveToFirst(); + Long mailboxId = c.getLong(ID_PROJECTION_COLUMN); + if (mailboxId != null && mailboxId.intValue() != 0) { + return mailboxId; + } else { + Log.w(Logging.LOG_TAG, "========== Mailbox of type " + type + " not found in cache??"); + } String[] bindArguments = new String[] {Long.toString(type), Long.toString(accountId)}; return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, ID_PROJECTION, WHERE_TYPE_AND_ACCOUNT_KEY, bindArguments, null, diff --git a/src/com/android/email/NotificationController.java b/src/com/android/email/NotificationController.java index b947e6f17..8fc8b80d3 100644 --- a/src/com/android/email/NotificationController.java +++ b/src/com/android/email/NotificationController.java @@ -16,28 +16,12 @@ package com.android.email; -import com.android.email.activity.ContactStatusLoader; -import com.android.email.activity.Welcome; -import com.android.email.activity.setup.AccountSecurity; -import com.android.email.activity.setup.AccountSettings; -import com.android.emailcommon.Logging; -import com.android.emailcommon.mail.Address; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.Attachment; -import com.android.emailcommon.provider.EmailContent.MailboxColumns; -import com.android.emailcommon.provider.EmailContent.Message; -import com.android.emailcommon.provider.EmailContent.MessageColumns; -import com.android.emailcommon.provider.Mailbox; -import com.android.emailcommon.utility.Utility; - -import com.google.common.annotations.VisibleForTesting; - import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ContentResolver; +import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; @@ -54,6 +38,22 @@ import android.text.TextUtils; import android.text.style.TextAppearanceSpan; import android.util.Log; +import com.android.email.activity.ContactStatusLoader; +import com.android.email.activity.Welcome; +import com.android.email.activity.setup.AccountSecurity; +import com.android.email.activity.setup.AccountSettings; +import com.android.emailcommon.Logging; +import com.android.emailcommon.mail.Address; +import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.Attachment; +import com.android.emailcommon.provider.EmailContent.MailboxColumns; +import com.android.emailcommon.provider.EmailContent.Message; +import com.android.emailcommon.provider.EmailContent.MessageColumns; +import com.android.emailcommon.provider.Mailbox; +import com.android.emailcommon.utility.Utility; +import com.google.common.annotations.VisibleForTesting; + import java.util.HashMap; import java.util.HashSet; @@ -608,10 +608,9 @@ public class NotificationController { ContentResolver resolver = mContext.getContentResolver(); long lastSeenMessageId = Utility.getFirstRowLong( - mContext, Mailbox.CONTENT_URI, + mContext, ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailboxId), new String[] { MailboxColumns.LAST_SEEN_MESSAGE_KEY }, - EmailContent.ID_SELECTION, - new String[] { Long.toString(mMailboxId) }, null, 0, 0L); + null, null, null, 0, 0L); Cursor c = resolver.query( Message.CONTENT_URI, EmailContent.ID_PROJECTION, MESSAGE_SELECTION, diff --git a/src/com/android/email/provider/ContentCache.java b/src/com/android/email/provider/ContentCache.java index ab3f37f28..492fc8e98 100644 --- a/src/com/android/email/provider/ContentCache.java +++ b/src/com/android/email/provider/ContentCache.java @@ -16,8 +16,6 @@ package com.android.email.provider; -import com.android.email.Email; - import android.content.ContentValues; import android.database.Cursor; import android.database.CursorWrapper; @@ -25,6 +23,10 @@ import android.database.MatrixCursor; import android.net.Uri; import android.util.Log; import android.util.LruCache; + +import com.android.email.Email; +import com.google.common.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -89,7 +91,7 @@ public final class ContentCache { private static final ArrayList sContentCaches = new ArrayList(); // A set of all unclosed, cached cursors; this will typically be a very small set, as cursors // tend to be closed quickly after use. The value, for each cursor, is its reference count - /*package*/ static CounterMap sActiveCursors; + /*package*/ static final CounterMap sActiveCursors = new CounterMap(24); // A set of locked content id's private final CounterMap mLockMap = new CounterMap(4); @@ -404,7 +406,6 @@ public final class ContentCache { mLogTag = "ContentCache-" + name; sContentCaches.add(this); mTokenList = new TokenList(mName); - sActiveCursors = new CounterMap(maxSize); mStats = new Statistics(this); } @@ -436,10 +437,14 @@ public final class ContentCache { return mLruCache.size(); } - private Cursor get(String id) { + @VisibleForTesting + Cursor get(String id) { return mLruCache.get(id); } + protected Map getSnapshot() { + return mLruCache.snapshot(); + } /** * Try to cache a cursor for the given id and projection; returns a valid cursor, either a * cached cursor (if caching was successful) or the original cursor @@ -456,7 +461,6 @@ public final class ContentCache { c.moveToPosition(0); return putCursorImpl(c, id, projection, token); } - public synchronized Cursor putCursorImpl(Cursor c, String id, String[] projection, CacheToken token) { try { @@ -467,7 +471,7 @@ public final class ContentCache { mStats.mStaleCount++; return c; } - if (c != null && projection == mBaseProjection && !sLockCache) { + if (c != null && Arrays.equals(projection, mBaseProjection) && !sLockCache) { if (Email.DEBUG && DEBUG_CACHE) { Log.d(mLogTag, "============ Caching cursor for: " + id); } @@ -723,7 +727,7 @@ public final class ContentCache { } // For use with unit tests - public static void invalidateAllCachesForTest() { + public static void invalidateAllCaches() { for (ContentCache cache: sContentCaches) { cache.invalidate(); } @@ -733,7 +737,7 @@ public final class ContentCache { public static void setLockCacheForTest(boolean lock) { sLockCache = lock; if (sLockCache) { - invalidateAllCachesForTest(); + invalidateAllCaches(); } } diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java index 3b2969074..6f6aac830 100644 --- a/src/com/android/email/provider/EmailProvider.java +++ b/src/com/android/email/provider/EmailProvider.java @@ -16,34 +16,6 @@ package com.android.email.provider; -import com.android.email.Email; -import com.android.email.Preferences; -import com.android.email.provider.ContentCache.CacheToken; -import com.android.email.service.AttachmentDownloadService; -import com.android.emailcommon.AccountManagerTypes; -import com.android.emailcommon.CalendarProviderStub; -import com.android.emailcommon.Logging; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.AccountColumns; -import com.android.emailcommon.provider.EmailContent.Attachment; -import com.android.emailcommon.provider.EmailContent.AttachmentColumns; -import com.android.emailcommon.provider.EmailContent.Body; -import com.android.emailcommon.provider.EmailContent.BodyColumns; -import com.android.emailcommon.provider.QuickResponse; -import com.android.emailcommon.provider.EmailContent.QuickResponseColumns; -import com.android.emailcommon.provider.EmailContent.HostAuthColumns; -import com.android.emailcommon.provider.EmailContent.MailboxColumns; -import com.android.emailcommon.provider.EmailContent.Message; -import com.android.emailcommon.provider.EmailContent.MessageColumns; -import com.android.emailcommon.provider.EmailContent.PolicyColumns; -import com.android.emailcommon.provider.EmailContent.SyncColumns; -import com.android.emailcommon.provider.HostAuth; -import com.android.emailcommon.provider.Mailbox; -import com.android.emailcommon.provider.Policy; -import com.android.emailcommon.service.LegacyPolicySet; -import com.google.common.annotations.VisibleForTesting; - import android.accounts.AccountManager; import android.content.ContentProvider; import android.content.ContentProviderOperation; @@ -66,8 +38,40 @@ import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log; +import com.android.email.Email; +import com.android.email.Preferences; +import com.android.email.provider.ContentCache.CacheToken; +import com.android.email.service.AttachmentDownloadService; +import com.android.emailcommon.AccountManagerTypes; +import com.android.emailcommon.CalendarProviderStub; +import com.android.emailcommon.Logging; +import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.AccountColumns; +import com.android.emailcommon.provider.EmailContent.Attachment; +import com.android.emailcommon.provider.EmailContent.AttachmentColumns; +import com.android.emailcommon.provider.EmailContent.Body; +import com.android.emailcommon.provider.EmailContent.BodyColumns; +import com.android.emailcommon.provider.EmailContent.HostAuthColumns; +import com.android.emailcommon.provider.EmailContent.MailboxColumns; +import com.android.emailcommon.provider.EmailContent.Message; +import com.android.emailcommon.provider.EmailContent.MessageColumns; +import com.android.emailcommon.provider.EmailContent.PolicyColumns; +import com.android.emailcommon.provider.EmailContent.QuickResponseColumns; +import com.android.emailcommon.provider.EmailContent.SyncColumns; +import com.android.emailcommon.provider.HostAuth; +import com.android.emailcommon.provider.Mailbox; +import com.android.emailcommon.provider.Policy; +import com.android.emailcommon.provider.QuickResponse; +import com.android.emailcommon.service.LegacyPolicySet; +import com.google.common.annotations.VisibleForTesting; + import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; public class EmailProvider extends ContentProvider { @@ -106,17 +110,24 @@ public class EmailProvider extends ContentProvider { private static final String WHERE_ID = EmailContent.RECORD_ID + "=?"; + // This is not a hard limit on accounts, per se, but beyond this, we can't guarantee that all + // critical mailboxes, host auth's, accounts, and policies are cached + private static final int MAX_CACHED_ACCOUNTS = 16; + // Inbox, Drafts, Sent, Outbox, Trash, and Search (these boxes are cached when possible) + private static final int NUM_ALWAYS_CACHED_MAILBOXES = 6; + // We'll cache the following four tables; sizes are best estimates of effective values - private static final ContentCache sCacheAccount = - new ContentCache("Account", Account.CONTENT_PROJECTION, 4); - private static final ContentCache sCacheHostAuth = - new ContentCache("HostAuth", HostAuth.CONTENT_PROJECTION, 8); - /*package*/ static final ContentCache sCacheMailbox = - new ContentCache("Mailbox", Mailbox.CONTENT_PROJECTION, 8); - private static final ContentCache sCacheMessage = + private final ContentCache mCacheAccount = + new ContentCache("Account", Account.CONTENT_PROJECTION, MAX_CACHED_ACCOUNTS); + private final ContentCache mCacheHostAuth = + new ContentCache("HostAuth", HostAuth.CONTENT_PROJECTION, MAX_CACHED_ACCOUNTS * 2); + /*package*/ final ContentCache mCacheMailbox = + new ContentCache("Mailbox", Mailbox.CONTENT_PROJECTION, + MAX_CACHED_ACCOUNTS * (NUM_ALWAYS_CACHED_MAILBOXES + 2)); + private final ContentCache mCacheMessage = new ContentCache("Message", Message.CONTENT_PROJECTION, 8); - private static final ContentCache sCachePolicy = - new ContentCache("Policy", Policy.CONTENT_PROJECTION, 4); + private final ContentCache mCachePolicy = + new ContentCache("Policy", Policy.CONTENT_PROJECTION, MAX_CACHED_ACCOUNTS); // Any changes to the database format *must* include update-in-place code. // Original version: 3 @@ -162,10 +173,12 @@ public class EmailProvider extends ContentProvider { private static final int ACCOUNT_ID_ADD_TO_FIELD = ACCOUNT_BASE + 2; private static final int ACCOUNT_RESET_NEW_COUNT = ACCOUNT_BASE + 3; private static final int ACCOUNT_RESET_NEW_COUNT_ID = ACCOUNT_BASE + 4; + private static final int ACCOUNT_DEFAULT_ID = ACCOUNT_BASE + 5; private static final int MAILBOX_BASE = 0x1000; private static final int MAILBOX = MAILBOX_BASE; private static final int MAILBOX_ID = MAILBOX_BASE + 1; + private static final int MAILBOX_ID_FROM_ACCOUNT_AND_TYPE = MAILBOX_BASE + 2; private static final int MAILBOX_ID_ADD_TO_FIELD = MAILBOX_BASE + 2; private static final int MESSAGE_BASE = 0x2000; @@ -214,32 +227,50 @@ public class EmailProvider extends ContentProvider { private static final String[] TABLE_NAMES = { Account.TABLE_NAME, Mailbox.TABLE_NAME, - EmailContent.Message.TABLE_NAME, - EmailContent.Attachment.TABLE_NAME, + Message.TABLE_NAME, + Attachment.TABLE_NAME, HostAuth.TABLE_NAME, - EmailContent.Message.UPDATED_TABLE_NAME, - EmailContent.Message.DELETED_TABLE_NAME, + Message.UPDATED_TABLE_NAME, + Message.DELETED_TABLE_NAME, Policy.TABLE_NAME, QuickResponse.TABLE_NAME, - EmailContent.Body.TABLE_NAME + Body.TABLE_NAME }; // CONTENT_CACHES MUST remain in the order of the BASE constants above - private static final ContentCache[] CONTENT_CACHES = { - sCacheAccount, - sCacheMailbox, - sCacheMessage, + private final ContentCache[] mContentCaches = { + mCacheAccount, + mCacheMailbox, + mCacheMessage, null, // Attachment - sCacheHostAuth, + mCacheHostAuth, null, // Updated message null, // Deleted message - sCachePolicy, + mCachePolicy, + null, // Quick response + null // Body + }; + + // CACHE_PROJECTIONS MUST remain in the order of the BASE constants above + private static final String[][] CACHE_PROJECTIONS = { + Account.CONTENT_PROJECTION, + Mailbox.CONTENT_PROJECTION, + Message.CONTENT_PROJECTION, + null, // Attachment + HostAuth.CONTENT_PROJECTION, + null, // Updated message + null, // Deleted message + Policy.CONTENT_PROJECTION, null, // Quick response null // Body }; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); + private static final String MAILBOX_PRE_CACHE_SELECTION = MailboxColumns.TYPE + " IN (" + + Mailbox.TYPE_INBOX + "," + Mailbox.TYPE_DRAFTS + "," + Mailbox.TYPE_TRASH + "," + + Mailbox.TYPE_SENT + "," + Mailbox.TYPE_SEARCH + "," + Mailbox.TYPE_OUTBOX + ")"; + /** * Let's only generate these SQL strings once, as they are used frequently * Note that this isn't relevant for table creation strings, since they are used only once @@ -301,6 +332,7 @@ public class EmailProvider extends ContentProvider { // A specific account // insert into this URI causes a mailbox to be added to the account matcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID); + matcher.addURI(EmailContent.AUTHORITY, "account/default", ACCOUNT_DEFAULT_ID); // Special URI to reset the new message count. Only update works, and content values // will be ignored. @@ -315,7 +347,8 @@ public class EmailProvider extends ContentProvider { // insert into this URI causes a message to be added to the mailbox // ** NOTE For now, the accountKey must be set manually in the values! matcher.addURI(EmailContent.AUTHORITY, "mailbox/#", MAILBOX_ID); - + matcher.addURI(EmailContent.AUTHORITY, "mailboxIdFromAccountAndType/#/#", + MAILBOX_ID_FROM_ACCOUNT_AND_TYPE); // All messages matcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE); // A specific message @@ -383,7 +416,6 @@ public class EmailProvider extends ContentProvider { QUICK_RESPONSE_ACCOUNT_ID); } - /** * Wrap the UriMatcher call so we can throw a runtime exception if an unknown Uri is passed in * @param uri the Uri to match @@ -778,10 +810,80 @@ public class EmailProvider extends ContentProvider { // Check for any orphaned Messages in the updated/deleted tables deleteOrphans(mDatabase, Message.UPDATED_TABLE_NAME); deleteOrphans(mDatabase, Message.DELETED_TABLE_NAME); - + if (Email.DEBUG) { + Log.d(TAG, "EmailProvider pre-caching..."); + } + preCacheData(); + if (Email.DEBUG) { + Log.d(TAG, "Pre-caching finished."); + } return mDatabase; } + /** + * Pre-cache all of the items in a given table meeting the selection criteria + * @param tableUri the table uri + * @param baseProjection the base projection of that table + * @param selection the selection criteria + */ + private void preCacheTable(Uri tableUri, String[] baseProjection, String selection) { + Cursor c = query(tableUri, EmailContent.ID_PROJECTION, selection, null, null); + try { + while (c.moveToNext()) { + long id = c.getLong(EmailContent.ID_PROJECTION_COLUMN); + Cursor cachedCursor = query(ContentUris.withAppendedId( + tableUri, id), baseProjection, null, null, null); + if (cachedCursor != null) { + // For accounts, create a mailbox type map entry (if necessary) + if (tableUri == Account.CONTENT_URI) { + getOrCreateAccountMailboxTypeMap(id); + } + cachedCursor.close(); + } + } + } finally { + c.close(); + } + } + + private HashMap> mMailboxTypeMap = + new HashMap>(); + + private synchronized HashMap getOrCreateAccountMailboxTypeMap(long accountId) { + HashMap accountMailboxTypeMap = mMailboxTypeMap.get(accountId); + if (accountMailboxTypeMap == null) { + accountMailboxTypeMap = new HashMap(); + mMailboxTypeMap.put(accountId, accountMailboxTypeMap); + } + return accountMailboxTypeMap; + } + + private synchronized void addToMailboxTypeMap(Cursor c) { + long accountId = c.getLong(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN); + int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN); + HashMap accountMailboxTypeMap = getOrCreateAccountMailboxTypeMap(accountId); + accountMailboxTypeMap.put(type, c.getLong(Mailbox.CONTENT_ID_COLUMN)); + } + + private void preCacheData() { + mMailboxTypeMap.clear(); + + // Pre-cache accounts, host auth's, policies, and special mailboxes + preCacheTable(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null); + preCacheTable(HostAuth.CONTENT_URI, HostAuth.CONTENT_PROJECTION, null); + preCacheTable(Policy.CONTENT_URI, Policy.CONTENT_PROJECTION, null); + preCacheTable(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, MAILBOX_PRE_CACHE_SELECTION); + + // Create a map from account,type to a mailbox + Map snapshot = mCacheMailbox.getSnapshot(); + Collection values = snapshot.values(); + if (values != null) { + for (Cursor c: values) { + addToMailboxTypeMap(c); + } + } + } + /*package*/ static SQLiteDatabase getReadableDatabase(Context context) { DatabaseHelper helper = new DatabaseHelper(context, DATABASE_NAME); return helper.getReadableDatabase(); @@ -1175,7 +1277,7 @@ public class EmailProvider extends ContentProvider { boolean messageDeletion = false; ContentResolver resolver = context.getContentResolver(); - ContentCache cache = CONTENT_CACHES[table]; + ContentCache cache = mContentCaches[table]; String tableName = TABLE_NAMES[table]; int result = -1; @@ -1231,17 +1333,23 @@ public class EmailProvider extends ContentProvider { case ACCOUNT_ID: // Account deletion will clear all of the caches, as HostAuth's, // Mailboxes, and Messages will be deleted in the process - sCacheMailbox.invalidate("Delete", uri, selection); - sCacheHostAuth.invalidate("Delete", uri, selection); + mCacheMailbox.invalidate("Delete", uri, selection); + mCacheHostAuth.invalidate("Delete", uri, selection); + mCachePolicy.invalidate("Delete", uri, selection); //$FALL-THROUGH$ case MAILBOX_ID: // Mailbox deletion will clear the Message cache - sCacheMessage.invalidate("Delete", uri, selection); + mCacheMessage.invalidate("Delete", uri, selection); //$FALL-THROUGH$ case SYNCED_MESSAGE_ID: case MESSAGE_ID: case HOSTAUTH_ID: + case POLICY_ID: cache.invalidate("Delete", uri, selection); + // Make sure all data is properly cached + if (match != MESSAGE_ID) { + preCacheData(); + } break; } } @@ -1270,18 +1378,29 @@ public class EmailProvider extends ContentProvider { switch(match) { // See the comments above for deletion of ACCOUNT_ID, etc case ACCOUNT: - sCacheMailbox.invalidate("Delete", uri, selection); - sCacheHostAuth.invalidate("Delete", uri, selection); + mCacheMailbox.invalidate("Delete", uri, selection); + mCacheHostAuth.invalidate("Delete", uri, selection); + mCachePolicy.invalidate("Delete", uri, selection); //$FALL-THROUGH$ case MAILBOX: - sCacheMessage.invalidate("Delete", uri, selection); + mCacheMessage.invalidate("Delete", uri, selection); //$FALL-THROUGH$ case MESSAGE: case HOSTAUTH: + case POLICY: cache.invalidate("Delete", uri, selection); break; } result = db.delete(tableName, selection, selectionArgs); + switch(match) { + case ACCOUNT: + case MAILBOX: + case HOSTAUTH: + case POLICY: + // Make sure all data is properly cached + preCacheData(); + break; + } break; default: @@ -1384,9 +1503,11 @@ public class EmailProvider extends ContentProvider { try { switch (match) { - case MESSAGE: + // NOTE: It is NOT legal for production code to insert directly into UPDATED_MESSAGE + // or DELETED_MESSAGE; see the comment below for details case UPDATED_MESSAGE: case DELETED_MESSAGE: + case MESSAGE: case BODY: case ATTACHMENT: case MAILBOX: @@ -1396,6 +1517,33 @@ public class EmailProvider extends ContentProvider { case QUICK_RESPONSE: longId = db.insert(TABLE_NAMES[table], "foo", values); resultUri = ContentUris.withAppendedId(uri, longId); + switch(match) { + case MAILBOX: + if (values.containsKey(MailboxColumns.TYPE)) { + // Only cache special mailbox types + int type = values.getAsInteger(MailboxColumns.TYPE); + if (type != Mailbox.TYPE_INBOX && type != Mailbox.TYPE_OUTBOX && + type != Mailbox.TYPE_DRAFTS && type != Mailbox.TYPE_SENT && + type != Mailbox.TYPE_TRASH && type != Mailbox.TYPE_SEARCH) { + break; + } + } + //$FALL-THROUGH$ + case ACCOUNT: + case HOSTAUTH: + case POLICY: + // Cache new account, host auth, policy, and some mailbox rows + Cursor c = query(resultUri, CACHE_PROJECTIONS[table], null, null, null); + if (c != null) { + if (match == MAILBOX) { + addToMailboxTypeMap(c); + } else if (match == ACCOUNT) { + getOrCreateAccountMailboxTypeMap(longId); + } + c.close(); + } + break; + } // Clients shouldn't normally be adding rows to these tables, as they are // maintained by triggers. However, we need to be able to do this for unit // testing, so we allow the insert and then throw the same exception that we @@ -1529,7 +1677,7 @@ public class EmailProvider extends ContentProvider { String tableName = TABLE_NAMES[table]; // We can only use the cache if there's no selection if (selection == null) { - cache = CONTENT_CACHES[table]; + cache = mContentCaches[table]; } if (cache == null) { ContentCache.notCacheable(uri, selection); @@ -1537,6 +1685,37 @@ public class EmailProvider extends ContentProvider { try { switch (match) { + case ACCOUNT_DEFAULT_ID: + // Start with a snapshot of the cache + Map accountCache = mCacheAccount.getSnapshot(); + long accountId = Account.NO_ACCOUNT; + // Find the account with "isDefault" set + Collection accounts = accountCache.values(); + int numAccounts = accounts.size(); + for (Cursor accountCursor: accounts) { + if (accountCursor.getInt(Account.CONTENT_IS_DEFAULT_COLUMN) == 1 || + numAccounts == 1) { + accountId = accountCursor.getLong(Account.CONTENT_ID_COLUMN); + break; + } + } + // Return a cursor with an id projection + MatrixCursor mc = new MatrixCursor(EmailContent.ID_PROJECTION); + mc.addRow(new Object[] {accountId}); + return mc; + case MAILBOX_ID_FROM_ACCOUNT_AND_TYPE: + // Get accountId and type and find the mailbox in our map + accountId = Long.parseLong(uri.getPathSegments().get(1)); + int type = Integer.parseInt(uri.getPathSegments().get(2)); + HashMap accountMap = mMailboxTypeMap.get(accountId); + mc = new MatrixCursor(EmailContent.ID_PROJECTION); + Long mailboxId = null; + if (accountMap != null) { + mailboxId = accountMap.get(type); + } + // Return a cursor with an id projection + mc.addRow(new Object[] {mailboxId}); + return mc; case BODY: case MESSAGE: case UPDATED_MESSAGE: @@ -1547,6 +1726,17 @@ public class EmailProvider extends ContentProvider { case HOSTAUTH: case POLICY: case QUICK_RESPONSE: + // Special-case "count of accounts"; it's common and we always know it + if (match == ACCOUNT && Arrays.equals(projection, EmailContent.COUNT_COLUMNS) && + selection == null && limit.equals("1")) { + int accountCount = mMailboxTypeMap.size(); + // In the rare case there are MAX_CACHED_ACCOUNTS or more, we can't do this + if (accountCount < MAX_CACHED_ACCOUNTS) { + mc = new MatrixCursor(projection, 1); + mc.addRow(new Object[] {accountCount}); + return mc; + } + } c = db.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, limit); break; @@ -1571,6 +1761,17 @@ public class EmailProvider extends ContentProvider { } c = db.query(tableName, projection, whereWithId(id, selection), selectionArgs, null, null, sortOrder, limit); + if (Email.DEBUG) { + switch(match) { + case ACCOUNT_ID: + case HOSTAUTH_ID: + case POLICY_ID: + case MAILBOX_ID: + Log.w(Logging.LOG_TAG, + "==== UNCACHED read of " + tableName + ", id = " + id); + break; + } + } if (cache != null) { c = cache.putCursor(c, id, projection, token); } @@ -1601,7 +1802,7 @@ public class EmailProvider extends ContentProvider { e.printStackTrace(); throw e; } finally { - if (cache != null && Email.DEBUG) { + if (cache != null && c != null && Email.DEBUG) { cache.recordQueryTime(c, System.nanoTime() - time); } } @@ -1820,11 +2021,12 @@ public class EmailProvider extends ContentProvider { values.remove(MailboxColumns.MESSAGE_COUNT); } - ContentCache cache = CONTENT_CACHES[table]; + ContentCache cache = mContentCaches[table]; String tableName = TABLE_NAMES[table]; String id = "0"; try { +outer: switch (match) { case MAILBOX_ID_ADD_TO_FIELD: case ACCOUNT_ID_ADD_TO_FIELD: @@ -1876,6 +2078,7 @@ public class EmailProvider extends ContentProvider { case ACCOUNT_ID: case HOSTAUTH_ID: case QUICK_RESPONSE_ID: + case POLICY_ID: id = uri.getPathSegments().get(1); if (cache != null) { cache.lock(id); @@ -1916,18 +2119,41 @@ public class EmailProvider extends ContentProvider { case MAILBOX: case ACCOUNT: case HOSTAUTH: + case POLICY: switch(match) { - case MESSAGE: + // To avoid invalidating the cache on updates, we execute them one at a + // time using the XXX_ID uri; these are all executed atomically case ACCOUNT: case MAILBOX: case HOSTAUTH: + case POLICY: + Cursor c = db.query(tableName, EmailContent.ID_PROJECTION, + selection, selectionArgs, null, null, null); + db.beginTransaction(); + result = 0; + try { + while (c.moveToNext()) { + update(ContentUris.withAppendedId( + uri, c.getLong(EmailContent.ID_PROJECTION_COLUMN)), + values, null, null); + result++; + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + c.close(); + } + break outer; + // Any cached table other than those above should be invalidated here + case MESSAGE: // If we're doing some generic update, the whole cache needs to be // invalidated. This case should be quite rare cache.invalidate("Update", uri, selection); - break; + //$FALL-THROUGH$ + default: + result = db.update(tableName, values, selection, selectionArgs); + break outer; } - result = db.update(tableName, values, selection, selectionArgs); - break; case ACCOUNT_RESET_NEW_COUNT_ID: id = uri.getPathSegments().get(1); if (cache != null) { @@ -2105,6 +2331,7 @@ public class EmailProvider extends ContentProvider { // Shouldn't be needed unless we're debugging and interrupt the process Log.w(TAG, "Exception upgrading EmailProvider.db from 17 to 18 " + e); } + ContentCache.invalidateAllCaches(); } /** Upgrades the database from v20 to v21 */ @@ -2206,4 +2433,20 @@ public class EmailProvider extends ContentProvider { Log.w(TAG, "Exception upgrading EmailProvider.db from 24 to 25 " + e); } } + + /** + * For testing purposes, check whether a given row is cached + * @param baseUri the base uri of the EmailContent + * @param id the row id of the EmailContent + * @return whether or not the row is currently cached + */ + @VisibleForTesting + protected boolean isCached(Uri baseUri, long id) { + int match = findMatch(baseUri, "isCached"); + int table = match >> BASE_SHIFT; + ContentCache cache = mContentCaches[table]; + if (cache == null) return false; + Cursor cc = cache.get(Long.toString(id)); + return (cc != null); + } } diff --git a/tests/src/com/android/email/ControllerProviderOpsTests.java b/tests/src/com/android/email/ControllerProviderOpsTests.java index a027c7ccd..229e34137 100644 --- a/tests/src/com/android/email/ControllerProviderOpsTests.java +++ b/tests/src/com/android/email/ControllerProviderOpsTests.java @@ -61,7 +61,7 @@ public class ControllerProviderOpsTests extends ProviderTestCase2 mContext = getContext(); mTestController = new TestController(mProviderContext, mContext); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } @Override diff --git a/tests/src/com/android/email/DBTestHelper.java b/tests/src/com/android/email/DBTestHelper.java index 029ff7384..0ba3eaa45 100644 --- a/tests/src/com/android/email/DBTestHelper.java +++ b/tests/src/com/android/email/DBTestHelper.java @@ -240,7 +240,7 @@ public final class DBTestHelper { ap.attachInfo(providerContext, null); resolver.addProvider(AttachmentUtilities.AUTHORITY, ap); - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); return providerContext; } diff --git a/tests/src/com/android/email/SecurityPolicyTests.java b/tests/src/com/android/email/SecurityPolicyTests.java index 1aa7c3840..930ea0a42 100644 --- a/tests/src/com/android/email/SecurityPolicyTests.java +++ b/tests/src/com/android/email/SecurityPolicyTests.java @@ -57,7 +57,7 @@ public class SecurityPolicyTests extends ProviderTestCase2 { super.setUp(); mMockContext = new MockContext2(getMockContext(), mContext); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } /** diff --git a/tests/src/com/android/email/activity/MailboxFinderTest.java b/tests/src/com/android/email/activity/MailboxFinderTest.java index ba1ee599f..6087081db 100644 --- a/tests/src/com/android/email/activity/MailboxFinderTest.java +++ b/tests/src/com/android/email/activity/MailboxFinderTest.java @@ -16,6 +16,11 @@ package com.android.email.activity; +import android.content.Context; +import android.test.InstrumentationTestCase; +import android.test.ProviderTestCase2; +import android.test.suitebuilder.annotation.LargeTest; + import com.android.email.Controller; import com.android.email.DBTestHelper; import com.android.email.Email; @@ -25,11 +30,6 @@ import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.Mailbox; -import android.content.Context; -import android.test.InstrumentationTestCase; -import android.test.ProviderTestCase2; -import android.test.suitebuilder.annotation.LargeTest; - /** * Test case for {@link MailboxFinder}. * diff --git a/tests/src/com/android/email/activity/MessageComposeTests.java b/tests/src/com/android/email/activity/MessageComposeTests.java index 7d5eee30b..17d0aadd7 100644 --- a/tests/src/com/android/email/activity/MessageComposeTests.java +++ b/tests/src/com/android/email/activity/MessageComposeTests.java @@ -16,18 +16,6 @@ package com.android.email.activity; -import com.android.email.Email; -import com.android.email.EmailAddressValidator; -import com.android.email.R; -import com.android.email.TestUtils; -import com.android.emailcommon.Logging; -import com.android.emailcommon.mail.Address; -import com.android.emailcommon.mail.MessagingException; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent.Attachment; -import com.android.emailcommon.provider.EmailContent.Message; -import com.google.android.collect.Lists; - import android.content.ContentUris; import android.content.Context; import android.content.Intent; @@ -41,6 +29,18 @@ import android.view.View; import android.widget.EditText; import android.widget.MultiAutoCompleteTextView; +import com.android.email.Email; +import com.android.email.EmailAddressValidator; +import com.android.email.R; +import com.android.email.TestUtils; +import com.android.emailcommon.Logging; +import com.android.emailcommon.mail.Address; +import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.EmailContent.Attachment; +import com.android.emailcommon.provider.EmailContent.Message; +import com.google.android.collect.Lists; + import java.util.ArrayList; diff --git a/tests/src/com/android/email/activity/RecentMailboxManagerTest.java b/tests/src/com/android/email/activity/RecentMailboxManagerTest.java index fbc60b878..38c177f6c 100644 --- a/tests/src/com/android/email/activity/RecentMailboxManagerTest.java +++ b/tests/src/com/android/email/activity/RecentMailboxManagerTest.java @@ -16,15 +16,15 @@ package com.android.email.activity; +import android.content.Context; +import android.test.AndroidTestCase; + import com.android.email.DBTestHelper; import com.android.email.MockClock; import com.android.email.provider.ContentCache; import com.android.email.provider.ProviderTestUtils; import com.android.emailcommon.provider.Mailbox; -import android.content.Context; -import android.test.AndroidTestCase; - import java.util.ArrayList; /** @@ -64,6 +64,8 @@ public class RecentMailboxManagerTest extends AndroidTestCase { ProviderTestUtils.setupMailbox("laurel", 1L, true, mMockContext, Mailbox.TYPE_MAIL), ProviderTestUtils.setupMailbox("hardy", 1L, true, mMockContext, Mailbox.TYPE_MAIL), }; + // Invalidate all caches, since we reset the database for each test + ContentCache.invalidateAllCaches(); } @Override diff --git a/tests/src/com/android/email/provider/PolicyTests.java b/tests/src/com/android/email/provider/PolicyTests.java index a8f7f3b4e..2903c45b4 100644 --- a/tests/src/com/android/email/provider/PolicyTests.java +++ b/tests/src/com/android/email/provider/PolicyTests.java @@ -55,7 +55,7 @@ public class PolicyTests extends ProviderTestCase2 { super.setUp(); mMockContext = getMockContext(); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } @Override diff --git a/tests/src/com/android/email/provider/ProviderTests.java b/tests/src/com/android/email/provider/ProviderTests.java index 2a013ec2e..d34bbbb93 100644 --- a/tests/src/com/android/email/provider/ProviderTests.java +++ b/tests/src/com/android/email/provider/ProviderTests.java @@ -16,23 +16,6 @@ package com.android.email.provider; -import com.android.emailcommon.AccountManagerTypes; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent; -import com.android.emailcommon.provider.EmailContent.AccountColumns; -import com.android.emailcommon.provider.EmailContent.Attachment; -import com.android.emailcommon.provider.EmailContent.AttachmentColumns; -import com.android.emailcommon.provider.EmailContent.Body; -import com.android.emailcommon.provider.EmailContent.BodyColumns; -import com.android.emailcommon.provider.EmailContent.MailboxColumns; -import com.android.emailcommon.provider.EmailContent.Message; -import com.android.emailcommon.provider.EmailContent.MessageColumns; -import com.android.emailcommon.provider.HostAuth; -import com.android.emailcommon.provider.Mailbox; -import com.android.emailcommon.utility.AccountReconciler; -import com.android.emailcommon.utility.TextUtilities; -import com.android.emailcommon.utility.Utility; - import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; @@ -49,6 +32,24 @@ import android.os.Parcel; import android.test.MoreAsserts; import android.test.ProviderTestCase2; +import com.android.emailcommon.AccountManagerTypes; +import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.AccountColumns; +import com.android.emailcommon.provider.EmailContent.Attachment; +import com.android.emailcommon.provider.EmailContent.AttachmentColumns; +import com.android.emailcommon.provider.EmailContent.Body; +import com.android.emailcommon.provider.EmailContent.BodyColumns; +import com.android.emailcommon.provider.EmailContent.MailboxColumns; +import com.android.emailcommon.provider.EmailContent.Message; +import com.android.emailcommon.provider.EmailContent.MessageColumns; +import com.android.emailcommon.provider.HostAuth; +import com.android.emailcommon.provider.Mailbox; +import com.android.emailcommon.provider.Policy; +import com.android.emailcommon.utility.AccountReconciler; +import com.android.emailcommon.utility.TextUtilities; +import com.android.emailcommon.utility.Utility; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -81,7 +82,7 @@ public class ProviderTests extends ProviderTestCase2 { mMockContext = getMockContext(); mProvider = getProvider(); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } @Override @@ -1737,9 +1738,11 @@ public class ProviderTests extends ProviderTestCase2 { public void testClearAccountHoldFlags() { Account a1 = ProviderTestUtils.setupAccount("holdflag-1", false, mMockContext); a1.mFlags = Account.FLAGS_NOTIFY_NEW_MAIL; + a1.mPolicy = new Policy(); a1.save(mMockContext); Account a2 = ProviderTestUtils.setupAccount("holdflag-2", false, mMockContext); a2.mFlags = Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_SECURITY_HOLD; + a2.mPolicy = new Policy(); a2.save(mMockContext); // bulk clear @@ -2120,7 +2123,7 @@ public class ProviderTests extends ProviderTestCase2 { public void testUpgradeFromVersion17ToVersion18() { final Context c = mMockContext; // Create accounts - Account a1 =createAccount(c, "exchange", + Account a1 = createAccount(c, "exchange", ProviderTestUtils.setupHostAuth("eas", "exchange.host.com", true, c), null); Account a2 = createAccount(c, "imap", @@ -2255,6 +2258,109 @@ public class ProviderTests extends ProviderTestCase2 { return false; } + public void testAutoCacheNewContent() { + Account account = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext); + // add hostauth data, which should be saved the first time + account.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-hostauth-recv", -1, false, + mMockContext); + account.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-hostauth-send", -1, false, + mMockContext); + account.save(mMockContext); + assertTrue(mProvider.isCached(Account.CONTENT_URI, account.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, account.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, account.mHostAuthSend.mId)); + } + + /** Creates a mailbox; redefine as we need version 17 mailbox values */ + private Mailbox createTypeMailbox(Context c, long accountId, int type) { + Mailbox box = new Mailbox(); + + box.mDisplayName = "foo"; + box.mServerId = "1:1"; + box.mParentKey = 0; + box.mAccountKey = accountId; + // Don't care about the fields below ... set them for giggles + box.mType = type; + box.save(c); + return box; + } + + public void testAutoCacheInvalidate() { + // Create 3 accounts with hostauth and 3 mailboxes each (2 of which are pre-cached) + Account a = ProviderTestUtils.setupAccount("account1", false, mMockContext); + a.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false, + mMockContext); + a.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-send", -1, false, + mMockContext); + a.save(mMockContext); + Mailbox a1 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_INBOX); + Mailbox a2 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_MAIL); + Mailbox a3 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_DRAFTS); + Account b = ProviderTestUtils.setupAccount("account2", false, mMockContext); + b.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false, + mMockContext); + b.mHostAuthSend = ProviderTestUtils.setupHostAuth("accoun-send", -1, false, + mMockContext); + b.save(mMockContext); + Mailbox b1 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_OUTBOX); + Mailbox b2 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_MAIL); + Mailbox b3 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_SENT); + Account c = ProviderTestUtils.setupAccount("account3", false, mMockContext); + c.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false, + mMockContext); + c.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-send", -1, false, + mMockContext); + c.save(mMockContext); + Mailbox c1 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_SEARCH); + Mailbox c2 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_MAIL); + Mailbox c3 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_TRASH); + + // Confirm expected cache state + assertTrue(mProvider.isCached(Account.CONTENT_URI, a.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthSend.mId)); + assertTrue(mProvider.isCached(Account.CONTENT_URI, b.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthSend.mId)); + assertTrue(mProvider.isCached(Account.CONTENT_URI, c.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthSend.mId)); + + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, a2.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a3.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, b1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b2.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, b3.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, c2.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c3.mId)); + + // Delete account b + EmailContent.delete(mMockContext, Account.CONTENT_URI, b.mId); + + // Confirm cache state + assertTrue(mProvider.isCached(Account.CONTENT_URI, a.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthSend.mId)); + assertFalse(mProvider.isCached(Account.CONTENT_URI, b.mId)); + assertFalse(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthRecv.mId)); + assertFalse(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthSend.mId)); + assertTrue(mProvider.isCached(Account.CONTENT_URI, c.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthRecv.mId)); + assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthSend.mId)); + + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, a2.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a3.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b2.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b3.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c1.mId)); + assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, c2.mId)); + assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c3.mId)); + } + /** * Remove a single pop/imap account from the AccountManager * @param accountManager our AccountManager diff --git a/tests/src/com/android/emailcommon/provider/MailboxTests.java b/tests/src/com/android/emailcommon/provider/MailboxTests.java index 8a6dec486..573e3d83a 100644 --- a/tests/src/com/android/emailcommon/provider/MailboxTests.java +++ b/tests/src/com/android/emailcommon/provider/MailboxTests.java @@ -60,7 +60,7 @@ public class MailboxTests extends ProviderTestCase2 { mMockContext = getMockContext(); mProvider = getProvider(); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } ////////////////////////////////////////////////////////// diff --git a/tests/src/com/android/emailcommon/provider/QuickResponseTests.java b/tests/src/com/android/emailcommon/provider/QuickResponseTests.java index b9258104f..f94136d78 100644 --- a/tests/src/com/android/emailcommon/provider/QuickResponseTests.java +++ b/tests/src/com/android/emailcommon/provider/QuickResponseTests.java @@ -16,23 +16,13 @@ package com.android.emailcommon.provider; -import com.android.email.provider.ContentCache; -import com.android.email.provider.EmailProvider; -import com.android.email.provider.ProviderTestUtils; -import com.android.emailcommon.provider.QuickResponse; -import com.android.emailcommon.utility.Utility; - -import android.content.ContentUris; -import android.content.ContentValues; import android.content.Context; -import android.net.Uri; import android.os.Parcel; -import android.test.MoreAsserts; import android.test.ProviderTestCase2; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; -import java.util.Arrays; +import com.android.email.provider.ContentCache; +import com.android.email.provider.EmailProvider; /** * Unit tests for the QuickResponse class @@ -52,7 +42,7 @@ public class QuickResponseTests extends ProviderTestCase2 { mMockContext = getMockContext(); mProvider = getProvider(); // Invalidate all caches, since we reset the database for each test - ContentCache.invalidateAllCachesForTest(); + ContentCache.invalidateAllCaches(); } public void testParcelling() {