Cleanups to Controller & AttachmentProvider

* New method in AttachmentProvider to delete all attachments for an
  account, and unit test it.
* New method in Controller to delete all synced data for an account,
  and unit test it.
* Fixed existing problem with Controller unit tests (needed cache clear)
* Fixed existing problems in Controller (mContext vs. mProviderContext)

Bug: 3197935
Change-Id: I79c6a03c21f18d37eeb8158cd1c2af0e0a6e9d2e
This commit is contained in:
Andy Stadler 2010-11-30 12:11:36 -08:00
parent 3bbc690600
commit e29189e3ee
4 changed files with 173 additions and 34 deletions

View File

@ -76,7 +76,6 @@ public class Controller {
/*package*/ final ConcurrentHashMap<Long, Boolean> mLegacyControllerMap =
new ConcurrentHashMap<Long, Boolean>();
// Note that 0 is a syntactically valid account key; however there can never be an account
// with id = 0, so attempts to restore the account will return null. Null values are
// handled properly within the code, so this won't cause any issues.
@ -98,11 +97,7 @@ public class Controller {
private static final int BODY_SOURCE_KEY_COLUMN = 0;
private static final String WHERE_MESSAGE_KEY = Body.MESSAGE_KEY + "=?";
private static final String[] MESSAGEID_TO_MAILBOXID_PROJECTION = new String[] {
EmailContent.RECORD_ID,
EmailContent.MessageColumns.MAILBOX_KEY
};
private static final int MESSAGEID_TO_MAILBOXID_COLUMN_MAILBOXID = 1;
private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
// Service callbacks as set up via setCallback
private static RemoteCallbackList<IEmailServiceCallback> sCallbackList =
@ -885,7 +880,7 @@ public class Controller {
// Flag the attachment as needing download at the user's request
ContentValues cv = new ContentValues();
cv.put(Attachment.FLAGS, attachInfo.mFlags | Attachment.FLAG_DOWNLOAD_USER_REQUEST);
attachInfo.update(mContext, cv);
attachInfo.update(mProviderContext, cv);
}
/**
@ -942,7 +937,7 @@ public class Controller {
public void deleteAccount(final long accountId) {
Utility.runAsync(new Runnable() {
public void run() {
deleteAccountSync(accountId, mContext);
deleteAccountSync(accountId, mProviderContext);
}
});
}
@ -969,7 +964,7 @@ public class Controller {
Uri uri = ContentUris.withAppendedId(
EmailContent.Account.CONTENT_URI, accountId);
mContext.getContentResolver().delete(uri, null, null);
context.getContentResolver().delete(uri, null, null);
// Update the backup (side copy) of the accounts
AccountBackupRestore.backupAccounts(context);
@ -989,6 +984,32 @@ public class Controller {
}
}
/**
* Delete all synced data, but don't delete the actual account. This is used when security
* policy requirements are not met, and we don't want to reveal any synced data, but we do
* wish to keep the account configured (e.g. to accept remote wipe commands).
*
* SYNCHRONOUS - do not call from UI thread.
*
* @param accountId The account to wipe.
*/
public void deleteSyncedDataSync(long accountId) {
try {
// Delete synced attachments
AttachmentProvider.deleteAllAccountAttachmentFiles(mProviderContext, accountId);
// Delete synced email
mProviderContext.getContentResolver().delete(Mailbox.CONTENT_URI,
MAILBOXES_FOR_ACCOUNT_SELECTION, new String[] { Long.toString(accountId) });
// Delete PIM data (contacts, calendar) if applicable
IEmailService service = getServiceForAccount(accountId);
if (service != null) {
service.deleteAccountPIMData(accountId);
}
} catch (Exception e) {
Log.w(Email.LOG_TAG, "Exception while deleting account synced data", e);
}
}
/**
* Simple callback for synchronous commands. For many commands, this can be largely ignored
* and the result is observed via provider cursors. The callback will *not* necessarily be

View File

@ -481,6 +481,22 @@ public class AttachmentProvider extends ContentProvider {
}
}
/**
* In support of deleting an account, delete all related attachments.
*
* @param context
* @param accountId the account for the mailbox
*/
public static void deleteAllAccountAttachmentFiles(Context context, long accountId) {
File[] files = getAttachmentDirectory(context, accountId).listFiles();
for (File file : files) {
boolean result = file.delete();
if (!result) {
Log.e(Email.LOG_TAG, "Failed to delete attachment file " + file.getName());
}
}
}
/**
* Need this to suppress warning in unit tests.
*/

View File

@ -16,6 +16,7 @@
package com.android.email;
import com.android.email.provider.ContentCache;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.ProviderTestUtils;
@ -59,6 +60,8 @@ public class ControllerProviderOpsTests extends ProviderTestCase2<EmailProvider>
mProviderContext = getMockContext();
mContext = getContext();
mTestController = new TestController(mProviderContext, mContext);
// Invalidate all caches, since we reset the database for each test
ContentCache.invalidateAllCachesForTest();
}
@Override
@ -374,6 +377,56 @@ public class ControllerProviderOpsTests extends ProviderTestCase2<EmailProvider>
Message.MAILBOX_KEY + "=?", new String[] {Long.toString(box.mId)}));
}
/**
* Test wiping an account's synced data (but not the account)
*/
public void testWipeSyncedData() {
Account account1 = ProviderTestUtils.setupAccount("wipe-synced-1", true, mProviderContext);
long account1Id = account1.mId;
Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mProviderContext);
long box1Id = box1.mId;
Mailbox box2 = ProviderTestUtils.setupMailbox("box2", account1Id, true, mProviderContext);
long box2Id = box2.mId;
Account account2 = ProviderTestUtils.setupAccount("wipe-synced-2", true, mProviderContext);
long account2Id = account2.mId;
Mailbox box3 = ProviderTestUtils.setupMailbox("box3", account2Id, true, mProviderContext);
long box3Id = box3.mId;
Mailbox box4 = ProviderTestUtils.setupMailbox("box4", account2Id, true, mProviderContext);
long box4Id = box4.mId;
// Now populate all 4 with messages
Message message = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
true, mProviderContext);
long message1Id = message.mId;
message = ProviderTestUtils.setupMessage("message2", account1Id, box2Id, false,
true, mProviderContext);
long message2Id = message.mId;
message = ProviderTestUtils.setupMessage("message3", account2Id, box3Id, false,
true, mProviderContext);
long message3Id = message.mId;
message = ProviderTestUtils.setupMessage("message4", account2Id, box4Id, false,
true, mProviderContext);
long message4Id = message.mId;
// Now wipe account 1's data
mTestController.deleteSyncedDataSync(account1Id);
// Confirm: Mailboxes gone, messages gone, account survives
assertNull(Mailbox.restoreMailboxWithId(mProviderContext, box1Id));
assertNull(Mailbox.restoreMailboxWithId(mProviderContext, box2Id));
assertNull(Message.restoreMessageWithId(mProviderContext, message1Id));
assertNull(Message.restoreMessageWithId(mProviderContext, message2Id));
assertNotNull(Account.restoreAccountWithId(mProviderContext, account1Id));
// Confirm: Other account survived
assertNotNull(Mailbox.restoreMailboxWithId(mProviderContext, box3Id));
assertNotNull(Mailbox.restoreMailboxWithId(mProviderContext, box4Id));
assertNotNull(Message.restoreMessageWithId(mProviderContext, message3Id));
assertNotNull(Message.restoreMessageWithId(mProviderContext, message4Id));
assertNotNull(Account.restoreAccountWithId(mProviderContext, account2Id));
}
public void testLoadMessageFromUri() throws Exception {
// Create a simple message
Message msg = new Message();

View File

@ -569,31 +569,9 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
Mailbox mailbox2 = ProviderTestUtils.setupMailbox("mbox2", account1Id, true, mMockContext);
long mailbox2Id = mailbox2.mId;
// two messages per mailbox, one w/attachments, one w/o attachments
Message message1a = ProviderTestUtils.setupMessage("msg1a", account1Id, mailbox1Id, false,
true, mMockContext);
Message message1b = ProviderTestUtils.setupMessage("msg1b", account1Id, mailbox1Id, false,
true, mMockContext);
Message message2a = ProviderTestUtils.setupMessage("msg2a", account1Id, mailbox2Id, false,
true, mMockContext);
Message message2b = ProviderTestUtils.setupMessage("msg2b", account1Id, mailbox2Id, false,
true, mMockContext);
// attachments on each of the "a" messages (3 on 1a, 1 on 1b)
Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1a.mId, "file1", 100,
true, mMockContext);
Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1a.mId, "file2", 200,
true, mMockContext);
Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1a.mId, "file3", 100,
true, mMockContext);
Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message2a.mId, "file4", 100,
true, mMockContext);
// Create test files
createAttachmentFile(account1, newAttachment1.mId);
createAttachmentFile(account1, newAttachment2.mId);
createAttachmentFile(account1, newAttachment3.mId);
createAttachmentFile(account1, newAttachment4.mId);
// Fill each mailbox with messages & attachments
populateAccountMailbox(account1, mailbox1Id, 3);
populateAccountMailbox(account1, mailbox2Id, 1);
// Confirm four attachment files found
File attachmentsDir = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
@ -608,6 +586,77 @@ public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvide
assertEquals(0, attachmentsDir.listFiles().length);
}
/**
* Test the functionality of deleting an entire account's attachments.
*/
public void testDeleteAccount() throws IOException {
Account account1 = ProviderTestUtils.setupAccount("attach-acct-del1", false, mMockContext);
account1.mCompatibilityUuid = "test-UUID";
account1.save(mMockContext);
long account1Id = account1.mId;
Mailbox mailbox1 = ProviderTestUtils.setupMailbox("mbox1", account1Id, true, mMockContext);
long mailbox1Id = mailbox1.mId;
Mailbox mailbox2 = ProviderTestUtils.setupMailbox("mbox2", account1Id, true, mMockContext);
long mailbox2Id = mailbox2.mId;
// Repeat for account #2
Account account2 = ProviderTestUtils.setupAccount("attach-acct-del2", false, mMockContext);
account2.mCompatibilityUuid = "test-UUID-2";
account2.save(mMockContext);
long account2Id = account2.mId;
Mailbox mailbox3 = ProviderTestUtils.setupMailbox("mbox3", account2Id, true, mMockContext);
long mailbox3Id = mailbox3.mId;
Mailbox mailbox4 = ProviderTestUtils.setupMailbox("mbox4", account2Id, true, mMockContext);
long mailbox4Id = mailbox4.mId;
// Fill each mailbox with messages & attachments
populateAccountMailbox(account1, mailbox1Id, 3);
populateAccountMailbox(account1, mailbox2Id, 1);
populateAccountMailbox(account2, mailbox3Id, 5);
populateAccountMailbox(account2, mailbox4Id, 2);
// Confirm eleven attachment files found
File directory1 = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
assertEquals(4, directory1.listFiles().length);
File directory2 = AttachmentProvider.getAttachmentDirectory(mMockContext, account2.mId);
assertEquals(7, directory2.listFiles().length);
// Command the deletion of account 1 - we should lose 4 attachment files
AttachmentProvider.deleteAllAccountAttachmentFiles(mMockContext, account1Id);
assertEquals(0, directory1.listFiles().length);
assertEquals(7, directory2.listFiles().length);
// Command the deletion of account 2 - we should lose 7 attachment file
AttachmentProvider.deleteAllAccountAttachmentFiles(mMockContext, account2Id);
assertEquals(0, directory1.listFiles().length);
assertEquals(0, directory2.listFiles().length);
}
/**
* Create a set of attachments for a given test account and mailbox. Creates the following:
* Two messages per mailbox, one w/attachments, one w/o attachments
* Any number of attachments (on the first message)
* @param account the account to populate
* @param mailboxId the mailbox to populate
* @param numAttachments how many attachments to create
*/
private void populateAccountMailbox(Account account, long mailboxId, int numAttachments)
throws IOException {
long accountId = account.mId;
// two messages per mailbox, one w/attachments, one w/o attachments
Message message1a = ProviderTestUtils.setupMessage(
"msg1a", accountId, mailboxId, false, true, mMockContext);
/* Message message1b = */ ProviderTestUtils.setupMessage(
"msg1b", accountId, mailboxId, false, true, mMockContext);
// Create attachment records & files
for (int count = 0; count < numAttachments; count++) {
Attachment newAttachment = ProviderTestUtils.setupAttachment(message1a.mId,
"file" + count, 100 * count, true, mMockContext);
createAttachmentFile(account, newAttachment.mId);
}
}
/**
* Create an attachment by copying an image resource into a file. Uses "real" resources