Simplify mailbox synchronization logic

The logic is simplified by creating database rows for new mailboxes down
in Imapstore. This means that the difference between local and remote folder
lists are mailboxes that need to be deleted.

Note -- this is still not the final CL. We probably update the database too
frequently and the column values aren't updated to support nested folders.

Change-Id: Ifbe4e0cf74ba81e5b6156b452ab72c56c35235ab
This commit is contained in:
Todd Kennedy 2011-04-20 16:22:41 -07:00
parent 33cfddf1fa
commit 200c6bd9fa
10 changed files with 230 additions and 127 deletions

View File

@ -16,6 +16,7 @@
package com.android.emailcommon.provider;
import com.android.emailcommon.Logging;
import com.android.emailcommon.utility.TextUtilities;
import com.android.emailcommon.utility.Utility;
@ -33,6 +34,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.net.URI;
@ -2250,6 +2252,10 @@ public abstract class EmailContent {
MailboxColumns.TYPE + " =?";
private static final String MAILBOX_TYPE_SELECTION =
MailboxColumns.TYPE + " =?";
/** Selection by display name for a given account */
private static final String NAME_AND_ACCOUNT_SELECTION =
MailboxColumns.DISPLAY_NAME + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
private static final String[] MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION = new String [] {
"sum(" + MailboxColumns.UNREAD_COUNT + ")"
};
@ -2387,6 +2393,38 @@ public abstract class EmailContent {
}
}
/**
* Returns a Mailbox from the database, given its pathname and account id. All mailbox
* paths for a particular account must be unique.
* @param context
* @param accountId the ID of the account
* @param path the fully qualified, remote pathname
*/
public static Mailbox restoreMailboxForPath(Context context, long accountId, String path) {
Cursor c = context.getContentResolver().query(
Mailbox.CONTENT_URI,
Mailbox.CONTENT_PROJECTION,
Mailbox.NAME_AND_ACCOUNT_SELECTION,
new String[] { path, Long.toString(accountId) },
null);
// TODO for mblank; uncomment when you submit CL Iab059f9a68eecd797914a6229f1ff9c03d0f0800
// if (c == null) throw new ProviderUnavailableException();
try {
Mailbox mailbox = null;
if (c.moveToFirst()) {
mailbox = getContent(c, Mailbox.class);
if (c.moveToNext()) {
Log.w(Logging.LOG_TAG, "Multiple mailboxes named \"" + path + "\"");
}
} else {
Log.i(Logging.LOG_TAG, "Could not find mailbox at \"" + path + "\"");
}
return mailbox;
} finally {
c.close();
}
}
@Override
public void restore(Cursor cursor) {
mBaseUri = CONTENT_URI;

View File

@ -36,6 +36,7 @@ import com.android.emailcommon.mail.Message;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.mail.Part;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
import com.android.emailcommon.provider.EmailContent.Mailbox;
@ -197,38 +198,23 @@ public class MessagingController implements Runnable {
return mListeners.isActiveListener(listener);
}
/**
* Lightweight class for capturing local mailboxes in an account. Just the columns
* necessary for a sync.
*/
private static class LocalMailboxInfo {
private static final int COLUMN_ID = 0;
private static final int COLUMN_DISPLAY_NAME = 1;
private static final int COLUMN_ACCOUNT_KEY = 2;
private static final int COLUMN_TYPE = 3;
private static final int MAILBOX_COLUMN_ID = 0;
private static final int MAILBOX_COLUMN_DISPLAY_NAME = 1;
private static final int MAILBOX_COLUMN_TYPE = 2;
private static final String[] PROJECTION = new String[] {
EmailContent.RECORD_ID,
MailboxColumns.DISPLAY_NAME, MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE,
};
final long mId;
final String mDisplayName;
final long mAccountKey;
final int mType;
public LocalMailboxInfo(Cursor c) {
mId = c.getLong(COLUMN_ID);
mDisplayName = c.getString(COLUMN_DISPLAY_NAME);
mAccountKey = c.getLong(COLUMN_ACCOUNT_KEY);
mType = c.getInt(COLUMN_TYPE);
}
}
/** Small projection for just the columns required for a sync. */
private static final String[] MAILBOX_PROJECTION = new String[] {
MailboxColumns.ID,
MailboxColumns.DISPLAY_NAME,
MailboxColumns.TYPE,
};
/**
* Asynchronously synchronize the folder list. If the specified {@link MessagingListener}
* is not {@code null}, it must have been previously added to the set of listeners using the
* {@link #addListener(MessagingListener)}. Otherwise, no actions will be performed.
* Synchronize the folder list with the remote server. Synchronization occurs in the
* background and results are passed through the {@link MessagingListener}. If the
* given listener is not {@code null}, it must have been previously added to the set
* of listeners using the {@link #addListener(MessagingListener)}. Otherwise, no
* actions will be performed.
*
* TODO this needs to cache the remote folder list
* TODO break out an inner listFoldersSynchronized which could simplify checkMail
@ -237,101 +223,71 @@ public class MessagingController implements Runnable {
* @param listener A listener to notify
*/
void listFolders(final long accountId, MessagingListener listener) {
final EmailContent.Account account =
EmailContent.Account.restoreAccountWithId(mContext, accountId);
final Account account = Account.restoreAccountWithId(mContext, accountId);
if (account == null) {
Log.i(Logging.LOG_TAG, "Could not load account id " + accountId
+ ". Has it been removed?");
return;
}
mListeners.listFoldersStarted(accountId);
put("listFolders", listener, new Runnable() {
// TODO For now, mailbox addition occurs in the server-dependent store implementation,
// but, mailbox removal occurs here. Instead, each store should be responsible for
// content synchronization (addition AND removal) since each store will likely need
// to implement it's own, unique synchronization methodology.
public void run() {
Cursor localFolderCursor = null;
try {
// Step 1: Get remote folders, make a list, and add any local folders
// that don't already exist.
// Step 1: Get remote mailboxes
Store store = Store.getInstance(account, mContext, null);
Folder[] remoteFolders = store.getAllFolders();
Folder[] remoteFolders = store.updateFolders();
HashSet<String> remoteFolderNames = new HashSet<String>();
for (int i = 0, count = remoteFolders.length; i < count; i++) {
remoteFolderNames.add(remoteFolders[i].getName());
}
HashMap<String, LocalMailboxInfo> localFolders =
new HashMap<String, LocalMailboxInfo>();
HashSet<String> localFolderNames = new HashSet<String>();
// Step 2: Get local mailboxes
localFolderCursor = mContext.getContentResolver().query(
EmailContent.Mailbox.CONTENT_URI,
LocalMailboxInfo.PROJECTION,
MAILBOX_PROJECTION,
EmailContent.MailboxColumns.ACCOUNT_KEY + "=?",
new String[] { String.valueOf(account.mId) },
null);
// Step 3: Remove any local mailbox not on the remote list
while (localFolderCursor.moveToNext()) {
LocalMailboxInfo info = new LocalMailboxInfo(localFolderCursor);
localFolders.put(info.mDisplayName, info);
localFolderNames.add(info.mDisplayName);
}
// Short circuit the rest if the sets are the same (the usual case)
if (!remoteFolderNames.equals(localFolderNames)) {
// They are different, so we have to do some adds and drops
// Drops first, to make things smaller rather than larger
HashSet<String> localsToDrop = new HashSet<String>(localFolderNames);
localsToDrop.removeAll(remoteFolderNames);
for (String localNameToDrop : localsToDrop) {
LocalMailboxInfo localInfo = localFolders.get(localNameToDrop);
// Exclusion list - never delete local special folders, irrespective
// of server-side existence.
switch (localInfo.mType) {
case Mailbox.TYPE_INBOX:
case Mailbox.TYPE_DRAFTS:
case Mailbox.TYPE_OUTBOX:
case Mailbox.TYPE_SENT:
case Mailbox.TYPE_TRASH:
break;
default:
// Drop all attachment files related to this mailbox
AttachmentUtilities.deleteAllMailboxAttachmentFiles(
mContext, accountId, localInfo.mId);
// Delete the mailbox. Triggers will take care of
// related Message, Body and Attachment records.
Uri uri = ContentUris.withAppendedId(
EmailContent.Mailbox.CONTENT_URI, localInfo.mId);
mContext.getContentResolver().delete(uri, null, null);
break;
}
String mailboxPath
= localFolderCursor.getString(MAILBOX_COLUMN_DISPLAY_NAME);
// Short circuit if we have a remote mailbox with the same name
if (remoteFolderNames.contains(mailboxPath)) {
continue;
}
// Now do the adds
remoteFolderNames.removeAll(localFolderNames);
for (String remoteNameToAdd : remoteFolderNames) {
EmailContent.Mailbox box = new EmailContent.Mailbox();
box.mDisplayName = remoteNameToAdd;
// box.mServerId;
// box.mParentServerId;
// box.mParentKey;
box.mAccountKey = account.mId;
box.mType = LegacyConversions.inferMailboxTypeFromName(
mContext, remoteNameToAdd);
// box.mDelimiter;
// box.mSyncKey;
// box.mSyncLookback;
// box.mSyncFrequency;
// box.mSyncTime;
// box.mUnreadCount;
box.mFlagVisible = true;
// box.mFlags;
box.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT;
box.save(mContext);
int mailboxType = localFolderCursor.getInt(MAILBOX_COLUMN_TYPE);
long mailboxId = localFolderCursor.getLong(MAILBOX_COLUMN_ID);
switch (mailboxType) {
case Mailbox.TYPE_INBOX:
case Mailbox.TYPE_DRAFTS:
case Mailbox.TYPE_OUTBOX:
case Mailbox.TYPE_SENT:
case Mailbox.TYPE_TRASH:
// Never, ever delete special mailboxes
break;
default:
// Drop all attachment files related to this mailbox
AttachmentUtilities.deleteAllMailboxAttachmentFiles(
mContext, accountId, mailboxId);
// Delete the mailbox; database triggers take care of related
// Message, Body and Attachment records
Uri uri = ContentUris.withAppendedId(
Mailbox.CONTENT_URI, mailboxId);
mContext.getContentResolver().delete(uri, null, null);
break;
}
}
mListeners.listFoldersFinished(accountId);
} catch (Exception e) {
mListeners.listFoldersFailed(accountId, "");
mListeners.listFoldersFailed(accountId, e.toString());
} finally {
if (localFolderCursor != null) {
localFolderCursor.close();

View File

@ -17,13 +17,14 @@
package com.android.email.mail;
import com.android.email.Email;
import com.android.email.LegacyConversions;
import com.android.email.R;
import com.android.emailcommon.Logging;
import com.android.emailcommon.mail.Folder;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import com.android.emailcommon.utility.Utility;
import com.android.emailcommon.provider.EmailContent.Mailbox;
import com.google.common.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParserException;
@ -35,6 +36,7 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
@ -274,7 +276,14 @@ public abstract class Store {
public abstract Folder getFolder(String name) throws MessagingException;
public abstract Folder[] getAllFolders() throws MessagingException;
/**
* Updates the local list of mailboxes according to what is located on the remote server.
* <em>Note: This does not perform folder synchronization and it will not remove mailboxes
* that are stored locally but not remotely.</em>
* @return The set of remote folders
* @throws MessagingException If there was a problem connecting to the remote server
*/
public abstract Folder[] updateFolders() throws MessagingException;
public abstract Bundle checkSettings() throws MessagingException;
@ -328,4 +337,59 @@ public abstract class Store {
throws MessagingException {
return null;
}
/**
* Returns a {@link Mailbox} for the given path. If the path is not in the database, a new
* mailbox will be created.
*/
private static Mailbox getMailboxForPath(Context context, long accountId, String path) {
Mailbox mailbox = Mailbox.restoreMailboxForPath(context, accountId, path);
if (mailbox == null) {
mailbox = new Mailbox();
}
return mailbox;
}
/**
* Adds the mailbox with the given path to the folder list and to the database. If the folder
* already exists on the server (e.g. the path is identical), the database row will be updated.
* Otherwise, a new database row will be inserted.
* @param folders the list of folders
* @param mailboxPath The path of the mailbox to add
* @param delimiter A path delimiter. May be {@code null} if there is no delimiter.
*/
protected void addMailbox(Context context, long accountId, String mailboxPath, String delimiter,
ArrayList<Folder> folders) throws MessagingException {
char delimiterChar = 0;
if (!TextUtils.isEmpty(delimiter)) {
delimiterChar = delimiter.charAt(0);
}
folders.add(getFolder(mailboxPath));
Mailbox mailbox = getMailboxForPath(context, accountId, mailboxPath);
mailbox.mAccountKey = accountId;
mailbox.mDelimiter = delimiterChar;
mailbox.mDisplayName = mailboxPath;
//mailbox.mFlags;
mailbox.mFlagVisible = true;
//mailbox.mParentKey;
//mailbox.mParentServerId;
mailbox.mServerId = mailboxPath;
//mailbox.mServerId;
//mailbox.mSyncFrequency;
//mailbox.mSyncKey;
//mailbox.mSyncLookback;
//mailbox.mSyncTime;
mailbox.mType = LegacyConversions.inferMailboxTypeFromName(context, mailboxPath);
//box.mUnreadCount;
mailbox.mVisibleLimit = Email.VISIBLE_LIMIT_DEFAULT;
// TODO This is horribly inefficient. Only update db if the mailbox has really changed
if (mailbox.isSaved()) {
mailbox.update(context, mailbox.toContentValues());
} else {
mailbox.save(context);
}
// TODO ?? Add mailbox to Folder object ??
}
}

View File

@ -80,7 +80,7 @@ public class ExchangeStore extends Store {
}
@Override
public Folder[] getAllFolders() {
public Folder[] updateFolders() {
return null;
}

View File

@ -93,6 +93,7 @@ public class ImapStore extends Store {
static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.FLAGGED };
private final Context mContext;
private final Account mAccount;
private Transport mRootTransport;
private String mUsername;
private String mPassword;
@ -140,6 +141,7 @@ public class ImapStore extends Store {
*/
private ImapStore(Context context, Account account) throws MessagingException {
mContext = context;
mAccount = account;
HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
if (recvAuth == null || !STORE_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) {
@ -359,7 +361,7 @@ public class ImapStore extends Store {
}
@Override
public Folder[] getAllFolders() throws MessagingException {
public Folder[] updateFolders() throws MessagingException {
ImapConnection connection = getConnection();
try {
ArrayList<Folder> folders = new ArrayList<Folder>();
@ -379,8 +381,8 @@ public class ImapStore extends Store {
// Get folder name.
ImapString encodedFolder = response.getStringOrEmpty(3);
if (encodedFolder.isEmpty()) continue;
String folder = decodeFolderName(encodedFolder.getString(), mPathPrefix);
if (ImapConstants.INBOX.equalsIgnoreCase(folder)) {
String folderName = decodeFolderName(encodedFolder.getString(), mPathPrefix);
if (ImapConstants.INBOX.equalsIgnoreCase(folderName)) {
continue;
}
@ -389,11 +391,12 @@ public class ImapStore extends Store {
includeFolder = false;
}
if (includeFolder) {
folders.add(getFolder(folder));
String delimiter = response.getStringOrEmpty(2).toString();
addMailbox(mContext, mAccount.mId, folderName, delimiter, folders);
}
}
}
folders.add(getFolder(ImapConstants.INBOX));
addMailbox(mContext, mAccount.mId, ImapConstants.INBOX, null, folders);
return folders.toArray(new Folder[] {});
} catch (IOException ioe) {
connection.close();

View File

@ -19,6 +19,7 @@ package com.android.email.mail.store;
import com.android.email.Email;
import com.android.email.mail.Store;
import com.android.email.mail.Transport;
import com.android.email.mail.store.imap.ImapConstants;
import com.android.email.mail.transport.MailTransport;
import com.android.emailcommon.Logging;
import com.android.emailcommon.internet.MimeMessage;
@ -53,6 +54,8 @@ public class Pop3Store extends Store {
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED };
private final Context mContext;
private final Account mAccount;
private Transport mTransport;
private String mUsername;
private String mPassword;
@ -95,6 +98,9 @@ public class Pop3Store extends Store {
* Creates a new store for the given account.
*/
private Pop3Store(Context context, Account account) throws MessagingException {
mContext = context;
mAccount = account;
HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
if (recvAuth == null || !STORE_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) {
throw new MessagingException("Unsupported protocol");
@ -148,10 +154,10 @@ public class Pop3Store extends Store {
}
@Override
public Folder[] getAllFolders() {
return new Folder[] {
getFolder("INBOX"),
};
public Folder[] updateFolders() throws MessagingException {
ArrayList<Folder> folders = new ArrayList<Folder>();
addMailbox(mContext, mAccount.mId, "INBOX", null, folders);
return folders.toArray(new Folder[] {});
}
/**

View File

@ -1212,7 +1212,7 @@ public class ImapStoreUnitTests extends InstrumentationTestCase {
"* lIST (\\HAsNoChildren) \"/\" \"&ZeVnLIqe-\"", // Japanese folder name
getNextTag(true) + " oK SUCCESS"
});
Folder[] folders = mStore.getAllFolders();
Folder[] folders = mStore.updateFolders();
ArrayList<String> list = new ArrayList<String>();
for (Folder f : folders) {
@ -2087,7 +2087,7 @@ public class ImapStoreUnitTests extends InstrumentationTestCase {
"* LIST () \"/\" \"" + FOLDER_2 + "\"",
getNextTag(true) + " OK SUCCESS"
});
final Folder[] folders = mStore.getAllFolders();
final Folder[] folders = mStore.updateFolders();
ArrayList<String> list = new ArrayList<String>();
for (Folder f : folders) {

View File

@ -246,10 +246,10 @@ public class Pop3StoreUnitTests extends InstrumentationTestCase {
/**
* Test small Store & Folder functions that manage folders & namespace
*/
public void testStoreFoldersFunctions() {
public void testStoreFoldersFunctions() throws MessagingException {
// getPersonalNamespaces() always returns INBOX folder
Folder[] folders = mStore.getAllFolders();
Folder[] folders = mStore.updateFolders();
assertEquals(1, folders.length);
assertSame(mFolder, folders[0]);
@ -326,8 +326,8 @@ public class Pop3StoreUnitTests extends InstrumentationTestCase {
/**
* Lightweight test to confirm that POP3 hasn't implemented any folder roles yet.
*/
public void testNoFolderRolesYet() {
Folder[] remoteFolders = mStore.getAllFolders();
public void testNoFolderRolesYet() throws MessagingException {
Folder[] remoteFolders = mStore.updateFolders();
for (Folder folder : remoteFolders) {
assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole());
}

View File

@ -117,9 +117,12 @@ public class ProviderTestUtils extends Assert {
Context context) {
return setupMailbox(name, accountId, saveIt, context, Mailbox.TYPE_MAIL);
}
public static Mailbox setupMailbox(String name, long accountId, boolean saveIt,
Context context, int type) {
return setupMailbox(name, accountId, saveIt, context, type, '/');
}
public static Mailbox setupMailbox(String name, long accountId, boolean saveIt,
Context context, int type, char delimiter) {
Mailbox box = new Mailbox();
box.mDisplayName = name;
@ -128,7 +131,7 @@ public class ProviderTestUtils extends Assert {
box.mParentKey = 4;
box.mAccountKey = accountId;
box.mType = type;
box.mDelimiter = 1;
box.mDelimiter = delimiter;
box.mSyncKey = "sync-key-" + name;
box.mSyncLookback = 2;
box.mSyncInterval = EmailContent.Account.CHECK_INTERVAL_NEVER;

View File

@ -2093,22 +2093,55 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
assertEquals(-1, Account.getAccountIdForMessageId(c, 12345));
}
public void testGetAccountMailboxFromMessageId() {
public void testGetMailboxForMessageId() {
final Context c = mMockContext;
Account a = ProviderTestUtils.setupAccount("acct", true, c);
Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a.mId, true, c, Mailbox.TYPE_MAIL);
Mailbox b2 = ProviderTestUtils.setupMailbox("box2", a.mId, true, c, Mailbox.TYPE_MAIL);
Mailbox b1 = ProviderTestUtils.setupMailbox("box1", 1, true, c, Mailbox.TYPE_MAIL);
Mailbox b2 = ProviderTestUtils.setupMailbox("box2", 1, true, c, Mailbox.TYPE_MAIL);
Message m1 = createMessage(c, b1, false, false);
Message m2 = createMessage(c, b2, false, false);
ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m1.mId));
ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m2.mId));
// Restore the mailboxes, since the unread & total counts will have changed
b1 = Mailbox.restoreMailboxWithId(c, b1.mId);
b2 = Mailbox.restoreMailboxWithId(c, b2.mId);
ProviderTestUtils.assertMailboxEqual("x", b1, Mailbox.getMailboxForMessageId(c, m1.mId));
ProviderTestUtils.assertMailboxEqual("x", b2, Mailbox.getMailboxForMessageId(c, m2.mId));
}
public void testRestoreMailboxWithId() {
final Context c = mMockContext;
Mailbox testMailbox;
testMailbox = ProviderTestUtils.setupMailbox("box1", 1, true, c, Mailbox.TYPE_MAIL);
ProviderTestUtils.assertMailboxEqual(
"x", testMailbox, Mailbox.restoreMailboxWithId(c, testMailbox.mId));
testMailbox = ProviderTestUtils.setupMailbox("box2", 1, true, c, Mailbox.TYPE_MAIL);
ProviderTestUtils.assertMailboxEqual(
"x", testMailbox, Mailbox.restoreMailboxWithId(c, testMailbox.mId));
// Unknown IDs
assertNull(Mailbox.restoreMailboxWithId(c, 8));
assertNull(Mailbox.restoreMailboxWithId(c, -1));
assertNull(Mailbox.restoreMailboxWithId(c, Long.MAX_VALUE));
}
public void testRestoreMailboxForPath() {
final Context c = mMockContext;
Mailbox testMailbox;
testMailbox = ProviderTestUtils.setupMailbox("a/b/c/box", 1, true, c, Mailbox.TYPE_MAIL);
ProviderTestUtils.assertMailboxEqual(
"x", testMailbox, Mailbox.restoreMailboxForPath(c, 1, "a/b/c/box"));
// Same name, different account; no match
assertNull(Mailbox.restoreMailboxForPath(c, 2, "a/b/c/box"));
// Substring; no match
assertNull(Mailbox.restoreMailboxForPath(c, 1, "a/b/c"));
// Wild cards not supported; no match
assertNull(Mailbox.restoreMailboxForPath(c, 1, "a/b/c/%"));
}
public void testGetAccountForMessageId() {
final Context c = mMockContext;
Account a = ProviderTestUtils.setupAccount("acct", true, c);
Message m1 = ProviderTestUtils.setupMessage("1", a.mId, 1, true, true, c, false, false);
Message m2 = ProviderTestUtils.setupMessage("1", a.mId, 2, true, true, c, false, false);
ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m1.mId));
ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m2.mId));
}
public void testGetAccountGetInboxIdTest() {
final Context c = mMockContext;