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:
parent
3bbc690600
commit
e29189e3ee
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue