Merge "Improve EmailContent caching..."

This commit is contained in:
Marc Blank 2011-06-21 23:46:37 -07:00 committed by Android (Google) Code Review
commit 0404a331ad
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() {