Create AccountManager acct for pop/imap on upgrade from GB

* Also, unit test for upgrade path

Bug: 4439595
Change-Id: I508a3d8ea70c1a894a412528314e42a39f3ae0e7
This commit is contained in:
Marc Blank 2011-05-19 14:14:14 -07:00
parent 5675ea88d3
commit f3ff0ba910
3 changed files with 182 additions and 1 deletions

View File

@ -18,6 +18,7 @@ package com.android.emailcommon.utility;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Account;
import com.google.common.annotations.VisibleForTesting;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
@ -32,6 +33,12 @@ import java.io.IOException;
import java.util.List;
public class AccountReconciler {
// AccountManager accounts with a name beginning with this constant are ignored for purposes
// of reconcilation. This is for unit test purposes only; the caller may NOT be in the same
// package as this class, so we make the constant public.
@VisibleForTesting
public static final String ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX = " _";
/**
* Compare our account list (obtained from EmailProvider) with the account list owned by
* AccountManager. If there are any orphans (an account in one list without a corresponding
@ -89,6 +96,9 @@ public class AccountReconciler {
found = true;
}
}
if (accountManagerAccountName.startsWith(ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX)) {
found = true;
}
if (!found) {
// This account has been deleted from the EmailProvider database
Log.d(Logging.LOG_TAG,

View File

@ -20,6 +20,7 @@ import com.android.email.Email;
import com.android.email.provider.ContentCache.CacheToken;
import com.android.email.service.AttachmentDownloadService;
import com.android.emailcommon.AccountManagerTypes;
import com.android.emailcommon.CalendarProviderStub;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
@ -58,6 +59,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.provider.ContactsContract;
import android.util.Log;
import java.io.File;
@ -137,7 +139,9 @@ public class EmailProvider extends ContentProvider {
// Account's policy when the Account is deleted
// Version 20: Add new policies to Policy table
// Version 21: Add lastSeenMessageKey column to Mailbox table
public static final int DATABASE_VERSION = 21;
// Version 22: Upgrade path for IMAP/POP accounts to integrate with AccountManager
public static final int DATABASE_VERSION = 22;
// Any changes to the database format *must* include update-in-place code.
// Original version: 2
@ -1070,6 +1074,10 @@ public class EmailProvider extends ContentProvider {
upgradeFromVersion20ToVersion21(db);
oldVersion = 21;
}
if (oldVersion == 21) {
upgradeFromVersion21ToVersion22(db, mContext);
oldVersion = 22;
}
}
@Override
@ -2005,4 +2013,61 @@ public class EmailProvider extends ContentProvider {
Log.w(TAG, "Exception upgrading EmailProvider.db from 20 to 21 " + e);
}
}
/**
* Upgrade the database from v21 to v22
* This entails creating AccountManager accounts for all pop3 and imap accounts
*/
private static final String[] RECV_PROJECTION =
new String[] {AccountColumns.HOST_AUTH_KEY_RECV};
private static final int EMAIL_AND_RECV_COLUMN_RECV = 0;
static private void createAccountManagerAccount(Context context, HostAuth hostAuth) {
AccountManager accountManager = AccountManager.get(context);
android.accounts.Account amAccount =
new android.accounts.Account(hostAuth.mLogin, AccountManagerTypes.TYPE_POP_IMAP);
accountManager.addAccountExplicitly(amAccount, hostAuth.mPassword, null);
ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(amAccount, EmailContent.AUTHORITY, true);
ContentResolver.setIsSyncable(amAccount, ContactsContract.AUTHORITY, 0);
ContentResolver.setIsSyncable(amAccount, CalendarProviderStub.AUTHORITY, 0);
}
@VisibleForTesting
static void upgradeFromVersion21ToVersion22(SQLiteDatabase db, Context accountManagerContext) {
try {
// Loop through accounts, looking for pop/imap accounts
Cursor accountCursor = db.query(Account.TABLE_NAME, RECV_PROJECTION, null,
null, null, null, null);
try {
String[] hostAuthArgs = new String[1];
while (accountCursor.moveToNext()) {
hostAuthArgs[0] = accountCursor.getString(EMAIL_AND_RECV_COLUMN_RECV);
// Get the "receive" HostAuth for this account
Cursor hostAuthCursor = db.query(HostAuth.TABLE_NAME,
HostAuth.CONTENT_PROJECTION, HostAuth.RECORD_ID + "=?", hostAuthArgs,
null, null, null);
try {
if (hostAuthCursor.moveToFirst()) {
HostAuth hostAuth = new HostAuth();
hostAuth.restore(hostAuthCursor);
String protocol = hostAuth.mProtocol;
// If this is a pop3 or imap account, create the account manager account
if ("imap".equals(protocol) || "pop3".equals(protocol)) {
createAccountManagerAccount(accountManagerContext, hostAuth);
}
}
} finally {
hostAuthCursor.close();
}
}
} finally {
accountCursor.close();
}
} catch (SQLException e) {
// Shouldn't be needed unless we're debugging and interrupt the process
Log.w(TAG, "Exception upgrading EmailProvider.db from 20 to 21 " + e);
}
}
}

View File

@ -16,6 +16,7 @@
package com.android.email.provider;
import com.android.emailcommon.AccountManagerTypes;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
@ -28,9 +29,13 @@ import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.AccountReconciler;
import com.android.emailcommon.utility.TextUtilities;
import com.android.emailcommon.utility.Utility;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@ -2229,4 +2234,105 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
assertEquals(Message.MAILBOX_KEY + "=" + out.mId,
Message.buildMessageListSelection(c, out.mId));
}
/**
* Determine whether a list of AccountManager accounts includes a given EmailProvider account
* @param amAccountList a list of AccountManager accounts
* @param account an EmailProvider account
* @param context the caller's context (our test provider's context)
* @return whether or not the EmailProvider account is represented in AccountManager
*/
private boolean amAccountListHasAccount(android.accounts.Account[] amAccountList,
Account account, Context context) {
HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
if (hostAuth == null) return false;
String login = hostAuth.mLogin;
for (android.accounts.Account amAccount: amAccountList) {
if (amAccount.name.equals(login)) {
return true;
}
}
return false;
}
/**
* Remove a single pop/imap account from the AccountManager
* @param accountManager our AccountManager
* @param name the name of the test account to remove
*/
private void removeAccountManagerAccount(AccountManager accountManager, String name) {
try {
accountManager.removeAccount(
new android.accounts.Account(name, AccountManagerTypes.TYPE_POP_IMAP),
null, null).getResult();
} catch (OperationCanceledException e) {
} catch (AuthenticatorException e) {
} catch (IOException e) {
}
}
/**
* Remove all test accounts from the AccountManager
* @param accountManager the AccountManager
*/
private void cleanupTestAccountManagerAccounts(AccountManager accountManager) {
android.accounts.Account[] amAccountList =
accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
for (android.accounts.Account account: amAccountList) {
if (account.name.startsWith(AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX)) {
removeAccountManagerAccount(accountManager, account.name);
}
}
}
/** Verifies updating the DB from v21 to v22 works as expected */
public void testUpgradeFromVersion21ToVersion22() {
String imapTestLogin =
AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX + "imap.host.com";
String pop3TestLogin =
AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX + "pop3.host.com";
AccountManager accountManager = AccountManager.get(mContext);
// Create provider accounts (one of each type)
Account a1 = createAccount(mMockContext, "exchange",
ProviderTestUtils.setupHostAuth("eas", "exchange.host.com", true, mMockContext),
null);
HostAuth h2 =
ProviderTestUtils.setupHostAuth("imap", "imap.host.com", false, mMockContext);
h2.mLogin = imapTestLogin;
h2.save(mMockContext);
Account a2 = createAccount(mMockContext, "imap", h2,
ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, mMockContext));
HostAuth h3 =
ProviderTestUtils.setupHostAuth("pop3", "pop3.host.com", false, mMockContext);
h3.mLogin = pop3TestLogin;
h3.save(mMockContext);
Account a3 = createAccount(mMockContext, "pop3", h3,
ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, mMockContext));
// Get the current list of AccountManager accounts (we have to use the real context here),
// whereas we use the mock context for EmailProvider (this is because the mock context
// doesn't implement AccountManager hooks)
android.accounts.Account[] amAccountList =
accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
// There shouldn't be AccountManager accounts for these
assertFalse(amAccountListHasAccount(amAccountList, a1, mMockContext));
assertFalse(amAccountListHasAccount(amAccountList, a2, mMockContext));
assertFalse(amAccountListHasAccount(amAccountList, a3, mMockContext));
amAccountList = null;
try {
// Upgrade the database
SQLiteDatabase db = getProvider().getDatabase(mMockContext);
EmailProvider.upgradeFromVersion21ToVersion22(db, getContext());
// The pop3 and imap account should now be in account manager
amAccountList = accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
assertFalse(amAccountListHasAccount(amAccountList, a1, mMockContext));
assertTrue(amAccountListHasAccount(amAccountList, a2, mMockContext));
assertTrue(amAccountListHasAccount(amAccountList, a3, mMockContext));
} finally {
cleanupTestAccountManagerAccounts(accountManager);
}
}
}