Improve EmailContent caching...

* Guarantee that up to 16 Account (with HostAuths), and Policy rows
  are always cached.  Also, 6 commonly used Mailboxes per Account
  (Inbox, Outbox, Drafts, Sent, Trash, and Search)
* Precache these rows when EmailProvider starts up
* Ensure that newly added, precachable rows are cached when created
* Clean up some inefficient/wrong caching code
* Fix a commonly called method in NotificationManager in which we
  load a single Mailbox row using selection vs withAppendedId
* Confirm that we don't read from the database in typical use and
  heavy message loading
* Add a special URI for finding mailbox by type (using the cache)
* Add special-case code for EmailContent.count(Account.CONTENT_URI)
  which is used in a number of places (including on the UI thread)
  and whose value is easily determined
* Add a special URI to get the default account id
* Confirm that all unit tests work

The goal here is to be able to load all Account, HostAuth, Policy,
and Mailbox objects (by id) without worrying about disk access.
There will still be a single disk read for uncommon Mailbox reads,
but this should be considered acceptable.

Change-Id: Ibc9aa7acc73185e360b0b6f3053b90a985e97210
TODO: Unit tests
This commit is contained in:
Marc Blank 2011-06-18 18:03:11 -07:00
parent 0fd968623d
commit 6e418aa41a
15 changed files with 526 additions and 161 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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<ContentCache> sContentCaches = new ArrayList<ContentCache>();
// 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<Cursor> sActiveCursors;
/*package*/ static final CounterMap<Cursor> sActiveCursors = new CounterMap<Cursor>(24);
// A set of locked content id's
private final CounterMap<String> mLockMap = new CounterMap<String>(4);
@ -404,7 +406,6 @@ public final class ContentCache {
mLogTag = "ContentCache-" + name;
sContentCaches.add(this);
mTokenList = new TokenList(mName);
sActiveCursors = new CounterMap<Cursor>(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<String, Cursor> 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();
}
}

View File

@ -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<Long, HashMap<Integer, Long>> mMailboxTypeMap =
new HashMap<Long, HashMap<Integer, Long>>();
private synchronized HashMap<Integer, Long> getOrCreateAccountMailboxTypeMap(long accountId) {
HashMap<Integer, Long> accountMailboxTypeMap = mMailboxTypeMap.get(accountId);
if (accountMailboxTypeMap == null) {
accountMailboxTypeMap = new HashMap<Integer, Long>();
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<Integer, Long> 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<String, Cursor> snapshot = mCacheMailbox.getSnapshot();
Collection<Cursor> 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<String, Cursor> accountCache = mCacheAccount.getSnapshot();
long accountId = Account.NO_ACCOUNT;
// Find the account with "isDefault" set
Collection<Cursor> 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<Integer, Long> 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);
}
}

View File

@ -61,7 +61,7 @@ public class ControllerProviderOpsTests extends ProviderTestCase2<EmailProvider>
mContext = getContext();
mTestController = new TestController(mProviderContext, mContext);
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
}
@Override

View File

@ -240,7 +240,7 @@ public final class DBTestHelper {
ap.attachInfo(providerContext, null);
resolver.addProvider(AttachmentUtilities.AUTHORITY, ap);
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
return providerContext;
}

View File

@ -57,7 +57,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
super.setUp();
mMockContext = new MockContext2(getMockContext(), mContext);
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
}
/**

View File

@ -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}.
*

View File

@ -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;

View File

@ -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

View File

@ -55,7 +55,7 @@ public class PolicyTests extends ProviderTestCase2<EmailProvider> {
super.setUp();
mMockContext = getMockContext();
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
}
@Override

View File

@ -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<EmailProvider> {
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<EmailProvider> {
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<EmailProvider> {
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<EmailProvider> {
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

View File

@ -60,7 +60,7 @@ public class MailboxTests extends ProviderTestCase2<EmailProvider> {
mMockContext = getMockContext();
mProvider = getProvider();
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
}
//////////////////////////////////////////////////////////

View File

@ -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<EmailProvider> {
mMockContext = getMockContext();
mProvider = getProvider();
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
ContentCache.invalidateAllCaches();
}
public void testParcelling() {