Improve synchronization in EmailProvider.

It's possible to have multiple EmailProviders.
Also get rid of references to the multiprocess attribute.

Bug: 10388165
Change-Id: Ic6be363eaee20b3b5deddc7b3054d1a7419483a1
This commit is contained in:
Yu Ping Hu 2013-08-20 14:54:42 -07:00
parent c0a1a81543
commit 4525565948
2 changed files with 134 additions and 123 deletions

View File

@ -340,7 +340,6 @@
<provider <provider
android:authorities="com.android.email2.conversation.provider" android:authorities="com.android.email2.conversation.provider"
android:label="@string/conversation_content_provider" android:label="@string/conversation_content_provider"
android:multiprocess="false"
android:exported="true" android:exported="true"
android:name="com.android.mail.browse.EmailConversationProvider" > android:name="com.android.mail.browse.EmailConversationProvider" >
<grant-uri-permission android:pathPattern=".*" /> <grant-uri-permission android:pathPattern=".*" />
@ -349,7 +348,6 @@
<provider <provider
android:authorities="com.android.email2.accountcache" android:authorities="com.android.email2.accountcache"
android:label="@string/account_cache_provider" android:label="@string/account_cache_provider"
android:multiprocess="false"
android:exported="true" android:exported="true"
android:name="com.android.mail.providers.EmailAccountCacheProvider" > android:name="com.android.mail.providers.EmailAccountCacheProvider" >
<grant-uri-permission android:pathPattern=".*" /> <grant-uri-permission android:pathPattern=".*" />
@ -357,7 +355,6 @@
<provider <provider
android:authorities="@string/eml_attachment_provider" android:authorities="@string/eml_attachment_provider"
android:multiprocess="false"
android:exported="false" android:exported="false"
android:name="com.android.mail.providers.EmlAttachmentProvider" > android:name="com.android.mail.providers.EmlAttachmentProvider" >
<grant-uri-permission android:pathPattern=".*" /> <grant-uri-permission android:pathPattern=".*" />
@ -627,7 +624,6 @@
<provider <provider
android:name=".provider.AttachmentProvider" android:name=".provider.AttachmentProvider"
android:authorities="com.android.email.attachmentprovider" android:authorities="com.android.email.attachmentprovider"
android:multiprocess="true"
android:grantUriPermissions="true" android:grantUriPermissions="true"
android:exported="true" android:exported="true"
android:readPermission="com.android.email.permission.READ_ATTACHMENT" android:readPermission="com.android.email.permission.READ_ATTACHMENT"
@ -638,7 +634,6 @@
<provider <provider
android:name=".provider.EmailProvider" android:name=".provider.EmailProvider"
android:authorities="com.android.email.provider;com.android.email.notifier" android:authorities="com.android.email.provider;com.android.email.notifier"
android:multiprocess="true"
android:exported="true" android:exported="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER" android:permission="com.android.email.permission.ACCESS_PROVIDER"
android:label="@string/app_name" android:label="@string/app_name"

View File

@ -254,7 +254,14 @@ public class EmailProvider extends ContentProvider {
TABLE_NAMES = array; TABLE_NAMES = array;
} }
private static UriMatcher sURIMatcher = null; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* Functions which manipulate the database connection or files synchronize on this.
* It's static because there can be multiple provider objects.
* TODO: Do we actually need to synchronize across all DB access, not just connection creation?
*/
private static final Object sDatabaseLock = new Object();
/** /**
* Let's only generate these SQL strings once, as they are used frequently * Let's only generate these SQL strings once, as they are used frequently
@ -348,40 +355,42 @@ public class EmailProvider extends ContentProvider {
} }
} }
private synchronized SQLiteDatabase getDatabase(Context context) { private SQLiteDatabase getDatabase(Context context) {
// Always return the cached database, if we've got one synchronized (sDatabaseLock) {
if (mDatabase != null) { // Always return the cached database, if we've got one
if (mDatabase != null) {
return mDatabase;
}
// Whenever we create or re-cache the databases, make sure that we haven't lost one
// to corruption
checkDatabases();
DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, DATABASE_NAME);
mDatabase = helper.getWritableDatabase();
DBHelper.BodyDatabaseHelper bodyHelper =
new DBHelper.BodyDatabaseHelper(context, BODY_DATABASE_NAME);
mBodyDatabase = bodyHelper.getWritableDatabase();
if (mBodyDatabase != null) {
String bodyFileName = mBodyDatabase.getPath();
mDatabase.execSQL("attach \"" + bodyFileName + "\" as BodyDatabase");
}
// Restore accounts if the database is corrupted...
restoreIfNeeded(context, mDatabase);
// Check for any orphaned Messages in the updated/deleted tables
deleteMessageOrphans(mDatabase, Message.UPDATED_TABLE_NAME);
deleteMessageOrphans(mDatabase, Message.DELETED_TABLE_NAME);
// Delete orphaned mailboxes/messages/policies (account no longer exists)
deleteUnlinked(mDatabase, Mailbox.TABLE_NAME, MailboxColumns.ACCOUNT_KEY,
AccountColumns.ID, Account.TABLE_NAME);
deleteUnlinked(mDatabase, Message.TABLE_NAME, MessageColumns.ACCOUNT_KEY,
AccountColumns.ID, Account.TABLE_NAME);
deleteUnlinked(mDatabase, Policy.TABLE_NAME, PolicyColumns.ID,
AccountColumns.POLICY_KEY, Account.TABLE_NAME);
initUiProvider();
return mDatabase; return mDatabase;
} }
// Whenever we create or re-cache the databases, make sure that we haven't lost one
// to corruption
checkDatabases();
DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, DATABASE_NAME);
mDatabase = helper.getWritableDatabase();
DBHelper.BodyDatabaseHelper bodyHelper =
new DBHelper.BodyDatabaseHelper(context, BODY_DATABASE_NAME);
mBodyDatabase = bodyHelper.getWritableDatabase();
if (mBodyDatabase != null) {
String bodyFileName = mBodyDatabase.getPath();
mDatabase.execSQL("attach \"" + bodyFileName + "\" as BodyDatabase");
}
// Restore accounts if the database is corrupted...
restoreIfNeeded(context, mDatabase);
// Check for any orphaned Messages in the updated/deleted tables
deleteMessageOrphans(mDatabase, Message.UPDATED_TABLE_NAME);
deleteMessageOrphans(mDatabase, Message.DELETED_TABLE_NAME);
// Delete orphaned mailboxes/messages/policies (account no longer exists)
deleteUnlinked(mDatabase, Mailbox.TABLE_NAME, MailboxColumns.ACCOUNT_KEY, AccountColumns.ID,
Account.TABLE_NAME);
deleteUnlinked(mDatabase, Message.TABLE_NAME, MessageColumns.ACCOUNT_KEY, AccountColumns.ID,
Account.TABLE_NAME);
deleteUnlinked(mDatabase, Policy.TABLE_NAME, PolicyColumns.ID, AccountColumns.POLICY_KEY,
Account.TABLE_NAME);
initUiProvider();
return mDatabase;
} }
/** /**
@ -836,7 +845,21 @@ public class EmailProvider extends ContentProvider {
public boolean onCreate() { public boolean onCreate() {
Context context = getContext(); Context context = getContext();
EmailContent.init(context); EmailContent.init(context);
if (INTEGRITY_CHECK_URI == null) { init(context);
// Do this last, so that EmailContent/EmailProvider are initialized
MailActivityEmail.setServicesEnabledAsync(context);
return false;
}
private static void init(final Context context) {
// Synchronize on the matcher rather than the class object to minimize risk of contention
// & deadlock.
synchronized (sURIMatcher) {
// We use the existence of this variable as indicative of whether this function has
// already run.
if (INTEGRITY_CHECK_URI != null) {
return;
}
INTEGRITY_CHECK_URI = Uri.parse("content://" + EmailContent.AUTHORITY + INTEGRITY_CHECK_URI = Uri.parse("content://" + EmailContent.AUTHORITY +
"/integrityCheck"); "/integrityCheck");
ACCOUNT_BACKUP_URI = ACCOUNT_BACKUP_URI =
@ -844,75 +867,66 @@ public class EmailProvider extends ContentProvider {
FOLDER_STATUS_URI = FOLDER_STATUS_URI =
Uri.parse("content://" + EmailContent.AUTHORITY + "/status"); Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
EMAIL_APP_MIME_TYPE = context.getString(R.string.application_mime_type); EMAIL_APP_MIME_TYPE = context.getString(R.string.application_mime_type);
}
checkDatabases();
initMatcher();
// Do this last, so that EmailContent/EmailProvider are initialized
MailActivityEmail.setServicesEnabledAsync(context);
return false;
}
private static synchronized void initMatcher() {
if (sURIMatcher == null) {
// Email URI matching table
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
// All accounts // All accounts
matcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT); sURIMatcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT);
// A specific account // A specific account
// insert into this URI causes a mailbox to be added to the account // insert into this URI causes a mailbox to be added to the account
matcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID);
matcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK); sURIMatcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK);
// Special URI to reset the new message count. Only update works, and values // Special URI to reset the new message count. Only update works, and values
// will be ignored. // will be ignored.
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount", ACCOUNT_RESET_NEW_COUNT); sURIMatcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount",
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount/#", ACCOUNT_RESET_NEW_COUNT);
sURIMatcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount/#",
ACCOUNT_RESET_NEW_COUNT_ID); ACCOUNT_RESET_NEW_COUNT_ID);
// All mailboxes // All mailboxes
matcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX); sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX);
// A specific mailbox // A specific mailbox
// insert into this URI causes a message to be added to the mailbox // 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! // ** NOTE For now, the accountKey must be set manually in the values!
matcher.addURI(EmailContent.AUTHORITY, "mailbox/*", MAILBOX_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox/*", MAILBOX_ID);
matcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#", MAILBOX_NOTIFICATION); sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#",
matcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#", MAILBOX_NOTIFICATION);
sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#",
MAILBOX_MOST_RECENT_MESSAGE); MAILBOX_MOST_RECENT_MESSAGE);
matcher.addURI(EmailContent.AUTHORITY, "mailboxCount/#", MAILBOX_MESSAGE_COUNT); sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxCount/#", MAILBOX_MESSAGE_COUNT);
// All messages // All messages
matcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE); sURIMatcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE);
// A specific message // A specific message
// insert into this URI causes an attachment to be added to the message // insert into this URI causes an attachment to be added to the message
matcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID);
// A specific attachment // A specific attachment
matcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT); sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT);
// A specific attachment (the header information) // A specific attachment (the header information)
matcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID);
// The attachments of a specific message (query only) (insert & delete TBD) // The attachments of a specific message (query only) (insert & delete TBD)
matcher.addURI(EmailContent.AUTHORITY, "attachment/message/#", ATTACHMENTS_MESSAGE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/message/#",
matcher.addURI(EmailContent.AUTHORITY, "attachment/cachedFile", ATTACHMENTS_MESSAGE_ID);
sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/cachedFile",
ATTACHMENTS_CACHED_FILE_ACCESS); ATTACHMENTS_CACHED_FILE_ACCESS);
// All mail bodies // All mail bodies
matcher.addURI(EmailContent.AUTHORITY, "body", BODY); sURIMatcher.addURI(EmailContent.AUTHORITY, "body", BODY);
// A specific mail body // A specific mail body
matcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID);
// All hostauth records // All hostauth records
matcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH); sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH);
// A specific hostauth // A specific hostauth
matcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID);
/** /**
* THIS URI HAS SPECIAL SEMANTICS * THIS URI HAS SPECIAL SEMANTICS
* ITS USE IS INTENDED FOR THE UI TO MARK CHANGES THAT NEED TO BE SYNCED BACK * ITS USE IS INTENDED FOR THE UI TO MARK CHANGES THAT NEED TO BE SYNCED BACK
* TO A SERVER VIA A SYNC ADAPTER * TO A SERVER VIA A SYNC ADAPTER
*/ */
matcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
matcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION); sURIMatcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION);
/** /**
* THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY * THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY
@ -920,55 +934,54 @@ public class EmailProvider extends ContentProvider {
* BY THE UI APPLICATION * BY THE UI APPLICATION
*/ */
// All deleted messages // All deleted messages
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE); sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE);
// A specific deleted message // A specific deleted message
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID);
// All updated messages // All updated messages
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE); sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE);
// A specific updated message // A specific updated message
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID);
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT = new ContentValues(); CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT = new ContentValues();
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT.put(Account.NEW_MESSAGE_COUNT, 0); CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT.put(Account.NEW_MESSAGE_COUNT, 0);
matcher.addURI(EmailContent.AUTHORITY, "policy", POLICY); sURIMatcher.addURI(EmailContent.AUTHORITY, "policy", POLICY);
matcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID);
// All quick responses // All quick responses
matcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE); sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE);
// A specific quick response // A specific quick response
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID);
// All quick responses associated with a particular account id // All quick responses associated with a particular account id
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#", sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#",
QUICK_RESPONSE_ACCOUNT_ID); QUICK_RESPONSE_ACCOUNT_ID);
matcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS);
matcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS);
matcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS);
matcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES); sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES);
matcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE); sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE);
matcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO);
matcher.addURI(EmailContent.AUTHORITY, QUERY_UIREFRESH + "/#", UI_FOLDER_REFRESH); sURIMatcher.addURI(EmailContent.AUTHORITY, QUERY_UIREFRESH + "/#", UI_FOLDER_REFRESH);
// We listen to everything trailing uifolder/ since there might be an appVersion // We listen to everything trailing uifolder/ since there might be an appVersion
// as in Utils.appendVersionQueryParameter(). // as in Utils.appendVersionQueryParameter().
matcher.addURI(EmailContent.AUTHORITY, "uifolder/*", UI_FOLDER); sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolder/*", UI_FOLDER);
matcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT);
matcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS);
matcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
matcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
matcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH); sURIMatcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
matcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
matcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE);
matcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION); sURIMatcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION);
matcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS); sURIMatcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS);
matcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#", sURIMatcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#",
UI_DEFAULT_RECENT_FOLDERS); UI_DEFAULT_RECENT_FOLDERS);
matcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#", ACCOUNT_PICK_TRASH_FOLDER); sURIMatcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#",
matcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#", ACCOUNT_PICK_SENT_FOLDER); ACCOUNT_PICK_TRASH_FOLDER);
sURIMatcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#",
// Do this last so if someone accidentally the synchronized keyword it will still work ACCOUNT_PICK_SENT_FOLDER);
sURIMatcher = matcher;
} }
} }
@ -978,27 +991,30 @@ public class EmailProvider extends ContentProvider {
* any "orphan" database, so that both will be created together. Note that an "orphan" database * any "orphan" database, so that both will be created together. Note that an "orphan" database
* will exist after either of the individual databases is deleted due to data corruption. * will exist after either of the individual databases is deleted due to data corruption.
*/ */
public synchronized void checkDatabases() { public void checkDatabases() {
// Uncache the databases synchronized (sDatabaseLock) {
if (mDatabase != null) { // Uncache the databases
mDatabase = null; if (mDatabase != null) {
} mDatabase = null;
if (mBodyDatabase != null) { }
mBodyDatabase = null; if (mBodyDatabase != null) {
} mBodyDatabase = null;
// Look for orphans, and delete as necessary; these must always be in sync }
final File databaseFile = getContext().getDatabasePath(DATABASE_NAME); // Look for orphans, and delete as necessary; these must always be in sync
final File bodyFile = getContext().getDatabasePath(BODY_DATABASE_NAME); final File databaseFile = getContext().getDatabasePath(DATABASE_NAME);
final File bodyFile = getContext().getDatabasePath(BODY_DATABASE_NAME);
// TODO Make sure attachments are deleted // TODO Make sure attachments are deleted
if (databaseFile.exists() && !bodyFile.exists()) { if (databaseFile.exists() && !bodyFile.exists()) {
LogUtils.w(TAG, "Deleting orphaned EmailProvider database..."); LogUtils.w(TAG, "Deleting orphaned EmailProvider database...");
getContext().deleteDatabase(DATABASE_NAME); getContext().deleteDatabase(DATABASE_NAME);
} else if (bodyFile.exists() && !databaseFile.exists()) { } else if (bodyFile.exists() && !databaseFile.exists()) {
LogUtils.w(TAG, "Deleting orphaned EmailProviderBody database..."); LogUtils.w(TAG, "Deleting orphaned EmailProviderBody database...");
getContext().deleteDatabase(BODY_DATABASE_NAME); getContext().deleteDatabase(BODY_DATABASE_NAME);
}
} }
} }
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) { String sortOrder) {