Move account delete logic to EmailProvider
* This functionality had to move away from Controller Change-Id: I557918a325eab8c83a9728fa1ce33dde8b86158f
This commit is contained in:
parent
ab6321e2c4
commit
ebb79619e8
|
@ -965,5 +965,4 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -637,4 +637,8 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
|
|||
return new Mailbox[size];
|
||||
}
|
||||
};
|
||||
|
||||
public String toString() {
|
||||
return "[Mailbox " + mId + ": " + mDisplayName + "]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.os.RemoteException;
|
|||
import android.util.Log;
|
||||
|
||||
import com.android.email.mail.store.Pop3Store.Pop3Message;
|
||||
import com.android.email.provider.AccountBackupRestore;
|
||||
import com.android.email.provider.Utilities;
|
||||
import com.android.email.service.EmailServiceUtils;
|
||||
import com.android.emailcommon.Logging;
|
||||
|
@ -92,12 +91,6 @@ public class Controller {
|
|||
};
|
||||
private static final int MESSAGEID_TO_ACCOUNTID_COLUMN_ACCOUNTID = 1;
|
||||
|
||||
private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
|
||||
private static final String MAILBOXES_FOR_ACCOUNT_EXCEPT_ACCOUNT_MAILBOX_SELECTION =
|
||||
MAILBOXES_FOR_ACCOUNT_SELECTION + " AND " + MailboxColumns.TYPE + "!=" +
|
||||
Mailbox.TYPE_EAS_ACCOUNT_MAILBOX;
|
||||
private static final String MESSAGES_FOR_ACCOUNT_SELECTION = MessageColumns.ACCOUNT_KEY + "=?";
|
||||
|
||||
// Service callbacks as set up via setCallback
|
||||
private static RemoteCallbackList<IEmailServiceCallback> sCallbackList =
|
||||
new RemoteCallbackList<IEmailServiceCallback>();
|
||||
|
@ -971,99 +964,6 @@ public class Controller {
|
|||
return getServiceForAccount(message.mAccountKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an account.
|
||||
*/
|
||||
public void deleteAccount(final long accountId) {
|
||||
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
deleteAccountSync(accountId, mProviderContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an account synchronously.
|
||||
*/
|
||||
public void deleteAccountSync(long accountId, Context context) {
|
||||
try {
|
||||
mLegacyControllerMap.remove(accountId);
|
||||
// Get the account URI.
|
||||
final Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account == null) {
|
||||
return; // Already deleted?
|
||||
}
|
||||
|
||||
// Delete account data, attachments, PIM data, etc.
|
||||
deleteSyncedDataSync(accountId);
|
||||
|
||||
// Now delete the account itself
|
||||
Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
|
||||
context.getContentResolver().delete(uri, null, null);
|
||||
|
||||
// For unit tests, don't run backup, security, and ui pieces.
|
||||
if (mInUnitTests) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
AccountBackupRestore.backup(context);
|
||||
SecurityPolicy.getInstance(context).reducePolicies();
|
||||
Email.setServicesEnabledSync(context);
|
||||
Email.setNotifyUiAccountsChanged(true);
|
||||
} catch (Exception e) {
|
||||
Log.w(Logging.LOG_TAG, "Exception while deleting account", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* The only mailbox not deleted is the account mailbox (if any)
|
||||
* Also, clear the sync keys on the remaining account, since the data is gone.
|
||||
*
|
||||
* SYNCHRONOUS - do not call from UI thread.
|
||||
*
|
||||
* @param accountId The account to wipe.
|
||||
*/
|
||||
public void deleteSyncedDataSync(long accountId) {
|
||||
try {
|
||||
// Delete synced attachments
|
||||
AttachmentUtilities.deleteAllAccountAttachmentFiles(mProviderContext,
|
||||
accountId);
|
||||
|
||||
// Delete synced email, leaving only an empty inbox. We do this in two phases:
|
||||
// 1. Delete all non-inbox mailboxes (which will delete all of their messages)
|
||||
// 2. Delete all remaining messages (which will be the inbox messages)
|
||||
ContentResolver resolver = mProviderContext.getContentResolver();
|
||||
String[] accountIdArgs = new String[] { Long.toString(accountId) };
|
||||
resolver.delete(Mailbox.CONTENT_URI,
|
||||
MAILBOXES_FOR_ACCOUNT_EXCEPT_ACCOUNT_MAILBOX_SELECTION,
|
||||
accountIdArgs);
|
||||
resolver.delete(Message.CONTENT_URI, MESSAGES_FOR_ACCOUNT_SELECTION, accountIdArgs);
|
||||
|
||||
// Delete sync keys on remaining items
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.putNull(Account.SYNC_KEY);
|
||||
resolver.update(Account.CONTENT_URI, cv, Account.ID_SELECTION, accountIdArgs);
|
||||
cv.clear();
|
||||
cv.putNull(Mailbox.SYNC_KEY);
|
||||
resolver.update(Mailbox.CONTENT_URI, cv,
|
||||
MAILBOXES_FOR_ACCOUNT_SELECTION, accountIdArgs);
|
||||
|
||||
// Delete PIM data (contacts, calendar), stop syncs, etc. if applicable
|
||||
IEmailService service = getServiceForAccount(accountId);
|
||||
if (service != null) {
|
||||
service.deleteAccountPIMData(accountId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(Logging.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
|
||||
|
|
|
@ -28,9 +28,11 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.service.EmailBroadcastProcessorService;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
|
@ -582,7 +584,8 @@ public class SecurityPolicy {
|
|||
NotificationController.getInstance(mContext).showSecurityUnsupportedNotification(
|
||||
account);
|
||||
// Erase data
|
||||
Controller.getInstance(mContext).deleteSyncedDataSync(accountId);
|
||||
Uri uri = EmailProvider.uiUri("uiaccountdata", accountId);
|
||||
mContext.getContentResolver().delete(uri, null, null);
|
||||
} else if (isActive(policy)) {
|
||||
if (policyChanged) {
|
||||
Log.d(Logging.LOG_TAG, "Notify policies for " + account.mDisplayName + " changed.");
|
||||
|
@ -661,8 +664,9 @@ public class SecurityPolicy {
|
|||
Log.w(TAG, "Email administration disabled; deleting " + c.getCount() +
|
||||
" secured account(s)");
|
||||
while (c.moveToNext()) {
|
||||
Controller.getInstance(context).deleteAccountSync(
|
||||
c.getLong(EmailContent.ID_PROJECTION_COLUMN), context);
|
||||
long accountId = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
|
||||
Uri uri = EmailProvider.uiUri("uiaccountdata", accountId);
|
||||
cr.delete(uri, null, null);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
|
@ -754,7 +758,8 @@ public class SecurityPolicy {
|
|||
// Mark the account as "on hold".
|
||||
setAccountHoldFlag(context, account, true);
|
||||
// Erase data
|
||||
controller.deleteSyncedDataSync(accountId);
|
||||
Uri uri = EmailProvider.uiUri("uiaccountdata", accountId);
|
||||
context.getContentResolver().delete(uri, null, null);
|
||||
// Report one or more were found
|
||||
result = true;
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@ import android.view.KeyEvent;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.android.email.Controller;
|
||||
import com.android.email.R;
|
||||
import com.android.email.activity.ActivityHelper;
|
||||
import com.android.email.mail.Sender;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
|
@ -678,11 +678,16 @@ public class AccountSettings extends PreferenceActivity {
|
|||
/**
|
||||
* Delete the selected account
|
||||
*/
|
||||
public void deleteAccount(Account account) {
|
||||
public void deleteAccount(final Account account) {
|
||||
// Kick off the work to actually delete the account
|
||||
// Delete the account (note, this is async. Would be nice to get a callback.
|
||||
Controller.getInstance(this).deleteAccount(account.mId);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Uri uri = EmailProvider.uiUri("uiaccount", account.mId);
|
||||
getContentResolver().delete(uri, null, null);
|
||||
}}).start();
|
||||
|
||||
// TODO: Remove ui glue for unified
|
||||
// Then update the UI as appropriate:
|
||||
// If single pane, return to the header list. If multi, rebuild header list
|
||||
if (onIsMultiPane()) {
|
||||
|
|
|
@ -21,9 +21,9 @@ import android.accounts.AccountManagerFuture;
|
|||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.Controller;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -119,8 +119,8 @@ public class AccountReconciler {
|
|||
Log.d(Logging.LOG_TAG,
|
||||
"Account deleted in AccountManager; deleting from provider: " +
|
||||
providerAccountName);
|
||||
Controller.getInstance(context).deleteAccountSync(providerAccount.mId,
|
||||
providerContext);
|
||||
Uri uri = EmailProvider.uiUri("uiaccount", providerAccount.mId);
|
||||
context.getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import com.android.common.content.ProjectionMap;
|
|||
import com.android.email.Email;
|
||||
import com.android.email.Preferences;
|
||||
import com.android.email.R;
|
||||
import com.android.email.SecurityPolicy;
|
||||
import com.android.email.provider.ContentCache.CacheToken;
|
||||
import com.android.email.service.AttachmentDownloadService;
|
||||
import com.android.email.service.EmailServiceUtils;
|
||||
|
@ -62,6 +63,7 @@ import com.android.emailcommon.provider.Mailbox;
|
|||
import com.android.emailcommon.provider.Policy;
|
||||
import com.android.emailcommon.provider.QuickResponse;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
import com.android.emailcommon.service.IEmailService;
|
||||
import com.android.emailcommon.service.IEmailServiceCallback;
|
||||
import com.android.emailcommon.service.SearchParams;
|
||||
import com.android.emailcommon.utility.AttachmentUtilities;
|
||||
|
@ -212,6 +214,7 @@ public class EmailProvider extends ContentProvider {
|
|||
private static final int UI_ATTACHMENTS = UI_BASE + 14;
|
||||
private static final int UI_ATTACHMENT = UI_BASE + 15;
|
||||
private static final int UI_SEARCH = UI_BASE + 16;
|
||||
private static final int UI_ACCOUNT_DATA = UI_BASE + 17;
|
||||
|
||||
// MUST ALWAYS EQUAL THE LAST OF THE PREVIOUS BASE CONSTANTS
|
||||
private static final int LAST_EMAIL_PROVIDER_DB_BASE = UI_BASE;
|
||||
|
@ -426,6 +429,7 @@ public class EmailProvider extends ContentProvider {
|
|||
matcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -446,6 +450,14 @@ public class EmailProvider extends ContentProvider {
|
|||
private SQLiteDatabase mDatabase;
|
||||
private SQLiteDatabase mBodyDatabase;
|
||||
|
||||
public static Uri uiUri(String type, long id) {
|
||||
return Uri.parse(uiUriString(type, id));
|
||||
}
|
||||
|
||||
public static String uiUriString(String type, long id) {
|
||||
return "content://" + EmailContent.AUTHORITY + "/" + type + ((id == -1) ? "" : ("/" + id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Orphan record deletion utility. Generates a sqlite statement like:
|
||||
* delete from <table> where <column> not in (select <foreignColumn> from <foreignTable>)
|
||||
|
@ -723,6 +735,10 @@ public class EmailProvider extends ContentProvider {
|
|||
switch (match) {
|
||||
case UI_MESSAGE:
|
||||
return uiDeleteMessage(uri);
|
||||
case UI_ACCOUNT_DATA:
|
||||
return uiDeleteAccountData(uri);
|
||||
case UI_ACCOUNT:
|
||||
return uiDeleteAccount(uri);
|
||||
// These are cases in which one or more Messages might get deleted, either by
|
||||
// cascade or explicitly
|
||||
case MAILBOX_ID:
|
||||
|
@ -2241,7 +2257,7 @@ outer:
|
|||
long mailboxId = Mailbox.findMailboxOfType(getContext(), accountId, Mailbox.TYPE_INBOX);
|
||||
if (mailboxId != Mailbox.NO_MAILBOX) {
|
||||
values.put(UIProvider.SettingsColumns.DEFAULT_INBOX,
|
||||
"content://" + EmailContent.AUTHORITY + "/uifolder/" + mailboxId);
|
||||
uiUriString("uifolder", mailboxId));
|
||||
}
|
||||
StringBuilder sb = genSelect(sAccountSettingsMap, uiProjection, values);
|
||||
sb.append(" FROM " + Account.TABLE_NAME + " WHERE " + AccountColumns.ID + "=?");
|
||||
|
@ -2520,7 +2536,7 @@ outer:
|
|||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
return Uri.parse("content://" + EmailContent.AUTHORITY + "/uimessage/" + msg.mId);
|
||||
return uiUri("uimessage", msg.mId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2913,4 +2929,86 @@ outer:
|
|||
return uiQuery(UI_FOLDER, ContentUris.withAppendedId(Mailbox.CONTENT_URI,
|
||||
searchMailbox.mId), projection);
|
||||
}
|
||||
|
||||
private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
|
||||
private static final String MAILBOXES_FOR_ACCOUNT_EXCEPT_ACCOUNT_MAILBOX_SELECTION =
|
||||
MAILBOXES_FOR_ACCOUNT_SELECTION + " AND " + MailboxColumns.TYPE + "!=" +
|
||||
Mailbox.TYPE_EAS_ACCOUNT_MAILBOX;
|
||||
private static final String MESSAGES_FOR_ACCOUNT_SELECTION = MessageColumns.ACCOUNT_KEY + "=?";
|
||||
|
||||
/**
|
||||
* Delete an account and clean it up
|
||||
*/
|
||||
private int uiDeleteAccount(Uri uri) {
|
||||
Context context = getContext();
|
||||
long accountId = Long.parseLong(uri.getLastPathSegment());
|
||||
try {
|
||||
// Get the account URI.
|
||||
final Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account == null) {
|
||||
return 0; // Already deleted?
|
||||
}
|
||||
|
||||
deleteAccountData(context, accountId);
|
||||
|
||||
// Now delete the account itself
|
||||
uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
|
||||
context.getContentResolver().delete(uri, null, null);
|
||||
|
||||
// Clean up
|
||||
AccountBackupRestore.backup(context);
|
||||
SecurityPolicy.getInstance(context).reducePolicies();
|
||||
Email.setServicesEnabledSync(context);
|
||||
return 1;
|
||||
} catch (Exception e) {
|
||||
Log.w(Logging.LOG_TAG, "Exception while deleting account", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int uiDeleteAccountData(Uri uri) {
|
||||
Context context = getContext();
|
||||
long accountId = Long.parseLong(uri.getLastPathSegment());
|
||||
// Get the account URI.
|
||||
final Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account == null) {
|
||||
return 0; // Already deleted?
|
||||
}
|
||||
deleteAccountData(context, accountId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void deleteAccountData(Context context, long accountId) {
|
||||
// Delete synced attachments
|
||||
AttachmentUtilities.deleteAllAccountAttachmentFiles(context, accountId);
|
||||
|
||||
// Delete synced email, leaving only an empty inbox. We do this in two phases:
|
||||
// 1. Delete all non-inbox mailboxes (which will delete all of their messages)
|
||||
// 2. Delete all remaining messages (which will be the inbox messages)
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
String[] accountIdArgs = new String[] { Long.toString(accountId) };
|
||||
resolver.delete(Mailbox.CONTENT_URI,
|
||||
MAILBOXES_FOR_ACCOUNT_EXCEPT_ACCOUNT_MAILBOX_SELECTION,
|
||||
accountIdArgs);
|
||||
resolver.delete(Message.CONTENT_URI, MESSAGES_FOR_ACCOUNT_SELECTION, accountIdArgs);
|
||||
|
||||
// Delete sync keys on remaining items
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.putNull(Account.SYNC_KEY);
|
||||
resolver.update(Account.CONTENT_URI, cv, Account.ID_SELECTION, accountIdArgs);
|
||||
cv.clear();
|
||||
cv.putNull(Mailbox.SYNC_KEY);
|
||||
resolver.update(Mailbox.CONTENT_URI, cv,
|
||||
MAILBOXES_FOR_ACCOUNT_SELECTION, accountIdArgs);
|
||||
|
||||
// Delete PIM data (contacts, calendar), stop syncs, etc. if applicable
|
||||
IEmailService service = EmailServiceUtils.getServiceForAccount(context, null, accountId);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.deleteAccountPIMData(accountId);
|
||||
} catch (RemoteException e) {
|
||||
// Can't do anything about this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -424,7 +424,8 @@ public class ControllerProviderOpsTests extends ProviderTestCase2<EmailProvider>
|
|||
long message4Id = message.mId;
|
||||
|
||||
// Now wipe account 1's data
|
||||
mTestController.deleteSyncedDataSync(account1Id);
|
||||
Uri uri = EmailProvider.uiUri("uiaccount", account1Id);
|
||||
mProviderContext.getContentResolver().delete(uri, null, null);
|
||||
|
||||
// Confirm: Mailboxes gone (except account box), all messages gone, account survives
|
||||
assertNull(Mailbox.restoreMailboxWithId(mProviderContext, box1Id));
|
||||
|
|
Loading…
Reference in New Issue