diff --git a/emailcommon/src/com/android/emailcommon/provider/Account.java b/emailcommon/src/com/android/emailcommon/provider/Account.java index b984035a3..615b47090 100755 --- a/emailcommon/src/com/android/emailcommon/provider/Account.java +++ b/emailcommon/src/com/android/emailcommon/provider/Account.java @@ -34,6 +34,10 @@ import android.os.Parcelable; import android.os.RemoteException; import com.android.emailcommon.utility.Utility; +import com.android.mail.utils.LogUtils; + +import org.json.JSONException; +import org.json.JSONObject; import java.util.ArrayList; @@ -132,12 +136,14 @@ public final class Account extends EmailContent implements Parcelable { @Deprecated private String mRingtoneUri; public String mProtocolVersion; - public int mNewMessageCount; public String mSecuritySyncKey; public String mSignature; public long mPolicyKey; public long mPingDuration; + private static final String JSON_TAG_HOST_AUTH_RECV = "hostAuthRecv"; + private static final String JSON_TAG_HOST_AUTH_SEND = "hostAuthSend"; + // Convenience for creating/working with an account public transient HostAuth mHostAuthRecv; public transient HostAuth mHostAuthSend; @@ -727,7 +733,6 @@ public final class Account extends EmailContent implements Parcelable { values.put(AccountColumns.SENDER_NAME, mSenderName); values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); - values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount); values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); values.put(AccountColumns.SIGNATURE, mSignature); values.put(AccountColumns.POLICY_KEY, mPolicyKey); @@ -735,6 +740,96 @@ public final class Account extends EmailContent implements Parcelable { return values; } + public String toJsonString(final Context context) { + ensureLoaded(context); + final JSONObject json = toJson(); + if (json != null) { + return json.toString(); + } + return null; + } + + protected JSONObject toJson() { + try { + final JSONObject json = new JSONObject(); + json.putOpt(AccountColumns.DISPLAY_NAME, mDisplayName); + json.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress); + json.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback); + json.put(AccountColumns.SYNC_INTERVAL, mSyncInterval); + final JSONObject recvJson = mHostAuthRecv.toJson(); + json.put(JSON_TAG_HOST_AUTH_RECV, recvJson); + if (mHostAuthSend != null) { + final JSONObject sendJson = mHostAuthSend.toJson(); + json.put(JSON_TAG_HOST_AUTH_SEND, sendJson); + } + json.put(AccountColumns.FLAGS, mFlags); + json.putOpt(AccountColumns.SENDER_NAME, mSenderName); + json.putOpt(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); + json.putOpt(AccountColumns.SIGNATURE, mSignature); + json.put(AccountColumns.PING_DURATION, mPingDuration); + return json; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while serializing Account"); + } + return null; + } + + public static Account fromJsonString(final String jsonString) { + try { + final JSONObject json = new JSONObject(jsonString); + return fromJson(json); + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Could not parse json for account"); + } + return null; + } + + protected static Account fromJson(final JSONObject json) { + try { + final Account a = new Account(); + a.mDisplayName = json.optString(AccountColumns.DISPLAY_NAME); + a.mEmailAddress = json.getString(AccountColumns.EMAIL_ADDRESS); + // SYNC_KEY is not stored + a.mSyncLookback = json.getInt(AccountColumns.SYNC_LOOKBACK); + a.mSyncInterval = json.getInt(AccountColumns.SYNC_INTERVAL); + final JSONObject recvJson = json.getJSONObject(JSON_TAG_HOST_AUTH_RECV); + a.mHostAuthRecv = HostAuth.fromJson(recvJson); + final JSONObject sendJson = json.optJSONObject(JSON_TAG_HOST_AUTH_SEND); + if (sendJson != null) { + a.mHostAuthSend = HostAuth.fromJson(sendJson); + } + a.mFlags = json.getInt(AccountColumns.FLAGS); + a.mSenderName = json.optString(AccountColumns.SENDER_NAME); + a.mProtocolVersion = json.optString(AccountColumns.PROTOCOL_VERSION); + // SECURITY_SYNC_KEY is not stored + a.mSignature = json.optString(AccountColumns.SIGNATURE); + // POLICY_KEY is not stored + a.mPingDuration = json.optInt(AccountColumns.PING_DURATION, 0); + return a; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while deserializing Account"); + } + return null; + } + + /** + * Ensure that all optionally-loaded fields are populated from the provider. + * @param context for provider loads + */ + public void ensureLoaded(final Context context) { + if (mHostAuthKeyRecv == 0 && mHostAuthRecv == null) { + throw new IllegalStateException("Trying to load incomplete Account object"); + } + getOrCreateHostAuthRecv(context).ensureLoaded(context); + + if (mHostAuthKeySend != 0) { + getOrCreateHostAuthSend(context); + if (mHostAuthSend != null) { + mHostAuthSend.ensureLoaded(context); + } + } + } + /** * Supports Parcelable */ @@ -778,7 +873,7 @@ public final class Account extends EmailContent implements Parcelable { dest.writeString(mSenderName); dest.writeString(mRingtoneUri); dest.writeString(mProtocolVersion); - dest.writeInt(mNewMessageCount); + dest.writeInt(0 /* mNewMessageCount */); dest.writeString(mSecuritySyncKey); dest.writeString(mSignature); dest.writeLong(mPolicyKey); @@ -816,7 +911,7 @@ public final class Account extends EmailContent implements Parcelable { mSenderName = in.readString(); mRingtoneUri = in.readString(); mProtocolVersion = in.readString(); - mNewMessageCount = in.readInt(); + /* mNewMessageCount = */ in.readInt(); mSecuritySyncKey = in.readString(); mSignature = in.readString(); mPolicyKey = in.readLong(); diff --git a/emailcommon/src/com/android/emailcommon/provider/Credential.java b/emailcommon/src/com/android/emailcommon/provider/Credential.java index d7961a29f..bf48693bb 100644 --- a/emailcommon/src/com/android/emailcommon/provider/Credential.java +++ b/emailcommon/src/com/android/emailcommon/provider/Credential.java @@ -9,10 +9,12 @@ import android.os.Parcelable; import android.provider.BaseColumns; import android.text.TextUtils; -import com.android.emailcommon.utility.Utility; import com.android.mail.utils.LogUtils; import com.google.common.base.Objects; +import org.json.JSONException; +import org.json.JSONObject; + public class Credential extends EmailContent implements Parcelable, BaseColumns { public static final String TABLE_NAME = "Credential"; @@ -143,9 +145,9 @@ public class Credential extends EmailContent implements Parcelable, BaseColumns return false; } Credential that = (Credential)o; - return Utility.areStringsEqual(mProviderId, that.mProviderId) - && Utility.areStringsEqual(mAccessToken, that.mAccessToken) - && Utility.areStringsEqual(mRefreshToken, that.mRefreshToken) + return TextUtils.equals(mProviderId, that.mProviderId) + && TextUtils.equals(mAccessToken, that.mAccessToken) + && TextUtils.equals(mRefreshToken, that.mRefreshToken) && mExpiration == that.mExpiration; } @@ -166,4 +168,32 @@ public class Credential extends EmailContent implements Parcelable, BaseColumns values.put(EXPIRATION_COLUMN, mExpiration); return values; } + + protected JSONObject toJson() { + try { + final JSONObject json = new JSONObject(); + json.put(PROVIDER_COLUMN, mProviderId); + json.putOpt(ACCESS_TOKEN_COLUMN, mAccessToken); + json.putOpt(REFRESH_TOKEN_COLUMN, mRefreshToken); + json.put(EXPIRATION_COLUMN, mExpiration); + return json; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while serializing Credential"); + } + return null; + } + + protected static Credential fromJson(final JSONObject json) { + try { + final Credential c = new Credential(); + c.mProviderId = json.getString(PROVIDER_COLUMN); + c.mAccessToken = json.optString(ACCESS_TOKEN_COLUMN); + c.mRefreshToken = json.optString(REFRESH_TOKEN_COLUMN); + c.mExpiration = json.optInt(EXPIRATION_COLUMN, 0); + return c; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while deserializing Credential"); + } + return null; + } } diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index 0df42913c..697db269e 100755 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -92,10 +92,6 @@ public abstract class EmailContent { public static final String ID_SELECTION = BaseColumns._ID + " =?"; - public static final String FIELD_COLUMN_NAME = "field"; - public static final String ADD_COLUMN_NAME = "add"; - public static final String SET_COLUMN_NAME = "set"; - public static final int SYNC_STATUS_NONE = UIProvider.SyncStatus.NO_SYNC; public static final int SYNC_STATUS_USER = UIProvider.SyncStatus.USER_REFRESH; public static final int SYNC_STATUS_BACKGROUND = UIProvider.SyncStatus.BACKGROUND_SYNC; @@ -1695,6 +1691,7 @@ public abstract class EmailContent { // Protocol version (arbitrary string, used by EAS currently) public static final String PROTOCOL_VERSION = "protocolVersion"; // The number of new messages (reported by the sync/download engines + @Deprecated public static final String NEW_MESSAGE_COUNT = "newMessageCount"; // Legacy flags defining security (provisioning) requirements of this account; this // information is now found in the Policy table; POLICY_KEY (below) is the foreign key diff --git a/emailcommon/src/com/android/emailcommon/provider/HostAuth.java b/emailcommon/src/com/android/emailcommon/provider/HostAuth.java index 14effc20a..b904c66da 100644 --- a/emailcommon/src/com/android/emailcommon/provider/HostAuth.java +++ b/emailcommon/src/com/android/emailcommon/provider/HostAuth.java @@ -25,9 +25,11 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import com.android.emailcommon.provider.EmailContent.HostAuthColumns; import com.android.emailcommon.utility.SSLUtils; -import com.android.emailcommon.utility.Utility; +import com.android.mail.utils.LogUtils; + +import org.json.JSONException; +import org.json.JSONObject; import java.net.URI; import java.net.URISyntaxException; @@ -69,6 +71,7 @@ public class HostAuth extends EmailContent implements Parcelable { public byte[] mServerCert = null; public long mCredentialKey; + private static final String JSON_TAG_CREDENTIAL = "credential"; public transient Credential mCredential; public static final int CONTENT_ID_COLUMN = 0; @@ -83,8 +86,8 @@ public class HostAuth extends EmailContent implements Parcelable { public static final int CONTENT_CREDENTIAL_KEY_COLUMN = 9; public static final String[] CONTENT_PROJECTION = new String[] { - RECORD_ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, HostAuthColumns.PORT, - HostAuthColumns.FLAGS, HostAuthColumns.LOGIN, + HostAuthColumns._ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, + HostAuthColumns.PORT, HostAuthColumns.FLAGS, HostAuthColumns.LOGIN, HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN, HostAuthColumns.CLIENT_CERT_ALIAS, HostAuthColumns.CREDENTIAL_KEY }; @@ -97,8 +100,8 @@ public class HostAuth extends EmailContent implements Parcelable { /** * Restore a HostAuth from the database, given its unique id - * @param context - * @param id + * @param context for provider loads + * @param id corresponds to rowid * @return the instantiated HostAuth */ public static HostAuth restoreHostAuthWithId(Context context, long id) { @@ -106,13 +109,6 @@ public class HostAuth extends EmailContent implements Parcelable { HostAuth.CONTENT_URI, HostAuth.CONTENT_PROJECTION, id); } - /** - * Returns the scheme for the specified flags. - */ - public static String getSchemeString(String protocol, int flags) { - return getSchemeString(protocol, flags, null); - } - /** * Returns the credential object for this HostAuth. This will load from the * database if the HosAuth has a valid credential key, or return null if not. @@ -131,7 +127,7 @@ public class HostAuth extends EmailContent implements Parcelable { * creating it if it does not yet exist. This should not be called on the * main thread. * - * @param context + * @param context for provider loads * @return the credential object for this HostAuth */ public Credential getOrCreateCredential(Context context) { @@ -157,7 +153,11 @@ public class HostAuth extends EmailContent implements Parcelable { * Builds a URI scheme name given the parameters for a {@code HostAuth}. If * a {@code clientAlias} is provided, this indicates that a secure * connection must be used. + * + * This is not used in live code, but is kept here for reference when creating providers.xml + * entries */ + @SuppressWarnings("unused") public static String getSchemeString(String protocol, int flags, String clientAlias) { String security = ""; switch (flags & USER_CONFIG_MASK) { @@ -244,6 +244,57 @@ public class HostAuth extends EmailContent implements Parcelable { return values; } + protected JSONObject toJson() { + try { + final JSONObject json = new JSONObject(); + json.put(HostAuthColumns.PROTOCOL, mProtocol); + json.put(HostAuthColumns.ADDRESS, mAddress); + json.put(HostAuthColumns.PORT, mPort); + json.put(HostAuthColumns.FLAGS, mFlags); + json.put(HostAuthColumns.LOGIN, mLogin); + json.putOpt(HostAuthColumns.PASSWORD, mPassword); + json.putOpt(HostAuthColumns.DOMAIN, mDomain); + json.putOpt(HostAuthColumns.CLIENT_CERT_ALIAS, mClientCertAlias); + if (mCredential != null) { + json.putOpt(JSON_TAG_CREDENTIAL, mCredential.toJson()); + } + return json; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while serializing HostAuth"); + } + return null; + } + + protected static HostAuth fromJson(final JSONObject json) { + try { + final HostAuth h = new HostAuth(); + h.mProtocol = json.getString(HostAuthColumns.PROTOCOL); + h.mAddress = json.getString(HostAuthColumns.ADDRESS); + h.mPort = json.getInt(HostAuthColumns.PORT); + h.mFlags = json.getInt(HostAuthColumns.FLAGS); + h.mLogin = json.getString(HostAuthColumns.LOGIN); + h.mPassword = json.optString(HostAuthColumns.PASSWORD); + h.mDomain = json.optString(HostAuthColumns.DOMAIN); + h.mClientCertAlias = json.optString(HostAuthColumns.CLIENT_CERT_ALIAS); + final JSONObject credJson = json.optJSONObject(JSON_TAG_CREDENTIAL); + if (credJson != null) { + h.mCredential = Credential.fromJson(credJson); + } + return h; + } catch (final JSONException e) { + LogUtils.d(LogUtils.TAG, e, "Exception while deserializing HostAuth"); + } + return null; + } + + /** + * Ensure that all optionally-loaded fields are populated from the provider. + * @param context for provider loads + */ + public void ensureLoaded(final Context context) { + getCredential(context); + } + /** * Sets the user name and password from URI user info string */ @@ -432,12 +483,12 @@ public class HostAuth extends EmailContent implements Parcelable { return mPort == that.mPort && mId == that.mId && mFlags == that.mFlags - && Utility.areStringsEqual(mProtocol, that.mProtocol) - && Utility.areStringsEqual(mAddress, that.mAddress) - && Utility.areStringsEqual(mLogin, that.mLogin) - && Utility.areStringsEqual(mPassword, that.mPassword) - && Utility.areStringsEqual(mDomain, that.mDomain) - && Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias); + && TextUtils.equals(mProtocol, that.mProtocol) + && TextUtils.equals(mAddress, that.mAddress) + && TextUtils.equals(mLogin, that.mLogin) + && TextUtils.equals(mPassword, that.mPassword) + && TextUtils.equals(mDomain, that.mDomain) + && TextUtils.equals(mClientCertAlias, that.mClientCertAlias); // We don't care about the server certificate for equals } diff --git a/emailcommon/src/com/android/emailcommon/utility/Utility.java b/emailcommon/src/com/android/emailcommon/utility/Utility.java index 91552ec03..55ac448dc 100644 --- a/emailcommon/src/com/android/emailcommon/utility/Utility.java +++ b/emailcommon/src/com/android/emailcommon/utility/Utility.java @@ -780,15 +780,6 @@ public class Utility { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } - /** - * Test that the given strings are equal in a null-pointer safe fashion. - */ - // Use TextUtils.equals() - @Deprecated - public static boolean areStringsEqual(String s1, String s2) { - return (s1 != null && s1.equals(s2)) || (s1 == null && s2 == null); - } - public static void enableStrictMode(boolean enabled) { StrictMode.setThreadPolicy(enabled ? new StrictMode.ThreadPolicy.Builder().detectAll().build() diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java index 30d3f1ea2..64db737f8 100644 --- a/src/com/android/email/provider/EmailProvider.java +++ b/src/com/android/email/provider/EmailProvider.java @@ -16,6 +16,7 @@ package com.android.email.provider; +import android.accounts.AccountManager; import android.appwidget.AppWidgetManager; import android.content.ComponentCallbacks; import android.content.ComponentName; @@ -66,6 +67,7 @@ import com.android.common.content.ProjectionMap; import com.android.email.Preferences; import com.android.email.R; import com.android.email.SecurityPolicy; +import com.android.email.activity.setup.AccountSettingsUtils; import com.android.email.service.AttachmentDownloadService; import com.android.email.service.EmailServiceUtils; import com.android.email.service.EmailServiceUtils.EmailServiceInfo; @@ -133,6 +135,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -164,7 +167,11 @@ public class EmailProvider extends ContentProvider { public static final String DATABASE_NAME = "EmailProvider.db"; public static final String BODY_DATABASE_NAME = "EmailProviderBody.db"; + // We don't back up to the backup database anymore, just keep this constant here so we can + // delete the old backups and trigger a new backup to the account manager + @Deprecated private static final String BACKUP_DATABASE_NAME = "EmailProviderBackup.db"; + private static final String ACCOUNT_MANAGER_JSON_TAG = "accountJson"; /** * Notifies that changes happened. Certain UI components, e.g., widgets, can register for this @@ -203,11 +210,9 @@ public class EmailProvider extends ContentProvider { private static final int ACCOUNT_BASE = 0; private static final int ACCOUNT = ACCOUNT_BASE; private static final int ACCOUNT_ID = ACCOUNT_BASE + 1; - private static final int ACCOUNT_RESET_NEW_COUNT = ACCOUNT_BASE + 2; - private static final int ACCOUNT_RESET_NEW_COUNT_ID = ACCOUNT_BASE + 3; - private static final int ACCOUNT_CHECK = ACCOUNT_BASE + 4; - private static final int ACCOUNT_PICK_TRASH_FOLDER = ACCOUNT_BASE + 5; - private static final int ACCOUNT_PICK_SENT_FOLDER = ACCOUNT_BASE + 6; + private static final int ACCOUNT_CHECK = ACCOUNT_BASE + 2; + private static final int ACCOUNT_PICK_TRASH_FOLDER = ACCOUNT_BASE + 3; + private static final int ACCOUNT_PICK_SENT_FOLDER = ACCOUNT_BASE + 4; private static final int MAILBOX_BASE = 0x1000; private static final int MAILBOX = MAILBOX_BASE; @@ -337,7 +342,6 @@ public class EmailProvider extends ContentProvider { private static final String DELETE_BODY = "delete from " + Body.TABLE_NAME + " where " + BodyColumns.MESSAGE_KEY + '='; - private static ContentValues CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT; private static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues(); private static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId"; @@ -530,6 +534,16 @@ public class EmailProvider extends ContentProvider { return; } + // If there's a backup database (old style) delete it and trigger an account manager backup. + // Roughly the same comment as above applies + final File backupDb = context.getDatabasePath(BACKUP_DATABASE_NAME); + if (backupDb.exists()) { + backupAccounts(context, mainDatabase); + context.deleteDatabase(BACKUP_DATABASE_NAME); + LogUtils.w(TAG, "Migrated from backup database to account manager"); + return; + } + // If we have accounts, we're done if (DatabaseUtils.longForQuery(mainDatabase, "SELECT EXISTS (SELECT ? FROM " + Account.TABLE_NAME + " )", @@ -540,7 +554,7 @@ public class EmailProvider extends ContentProvider { return; } - restoreAccounts(context, mainDatabase); + restoreAccounts(context); } /** {@inheritDoc} */ @@ -1041,13 +1055,6 @@ public class EmailProvider extends ContentProvider { sURIMatcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID); sURIMatcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK); - // Special URI to reset the new message count. Only update works, and values - // will be ignored. - sURIMatcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount", - ACCOUNT_RESET_NEW_COUNT); - sURIMatcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount/#", - ACCOUNT_RESET_NEW_COUNT_ID); - // All mailboxes sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX); // A specific mailbox @@ -1122,9 +1129,6 @@ public class EmailProvider extends ContentProvider { // A specific updated message sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID); - CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT = new ContentValues(); - CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT.put(AccountColumns.NEW_MESSAGE_COUNT, 0); - sURIMatcher.addURI(EmailContent.AUTHORITY, "policy", POLICY); sURIMatcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID); @@ -1531,57 +1535,66 @@ public class EmailProvider extends ContentProvider { return copyCount; } - private static SQLiteDatabase getBackupDatabase(Context context) { - DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, BACKUP_DATABASE_NAME); - return helper.getWritableDatabase(); - } - /** * Backup account data, returning the number of accounts backed up */ - private static int backupAccounts(Context context, SQLiteDatabase mainDatabase) { - if (MailActivityEmail.DEBUG) { - LogUtils.d(TAG, "backupAccounts..."); - } - SQLiteDatabase backupDatabase = getBackupDatabase(context); + private static int backupAccounts(final Context context, final SQLiteDatabase db) { + final AccountManager am = AccountManager.get(context); + final Cursor accountCursor = db.query(Account.TABLE_NAME, Account.CONTENT_PROJECTION, + null, null, null, null, null); + int updatedCount = 0; try { - int numBackedUp = copyAccountTables(mainDatabase, backupDatabase); - if (numBackedUp < 0) { - LogUtils.e(TAG, "Account backup failed!"); - } else if (MailActivityEmail.DEBUG) { - LogUtils.d(TAG, "Backed up " + numBackedUp + " accounts..."); + while (accountCursor.moveToNext()) { + final Account account = new Account(); + account.restore(accountCursor); + EmailServiceInfo serviceInfo = + EmailServiceUtils.getServiceInfo(context, account.getProtocol(context)); + if (serviceInfo == null) { + LogUtils.d(LogUtils.TAG, "Could not find service info for account"); + continue; + } + final String jsonString = account.toJsonString(context); + final android.accounts.Account amAccount = + account.getAccountManagerAccount(serviceInfo.accountType); + am.setUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG, jsonString); + updatedCount++; } - return numBackedUp; } finally { - if (backupDatabase != null) { - backupDatabase.close(); - } + accountCursor.close(); } + return updatedCount; } /** * Restore account data, returning the number of accounts restored */ - private static int restoreAccounts(Context context, SQLiteDatabase mainDatabase) { - if (MailActivityEmail.DEBUG) { - LogUtils.d(TAG, "restoreAccounts..."); + private static int restoreAccounts(final Context context) { + final Collection infos = EmailServiceUtils.getServiceInfoList(context); + // Find all possible account types + final Set accountTypes = new HashSet(3); + for (final EmailServiceInfo info : infos) { + accountTypes.add(info.accountType); } - SQLiteDatabase backupDatabase = getBackupDatabase(context); - try { - int numRecovered = copyAccountTables(backupDatabase, mainDatabase); - if (numRecovered > 0) { - LogUtils.e(TAG, "Recovered " + numRecovered + " accounts!"); - } else if (numRecovered < 0) { - LogUtils.e(TAG, "Account recovery failed?"); - } else if (MailActivityEmail.DEBUG) { - LogUtils.d(TAG, "No accounts to restore..."); + // Find all accounts we own + final List amAccounts = new ArrayList(); + final AccountManager am = AccountManager.get(context); + for (final String accountType : accountTypes) { + amAccounts.addAll(Arrays.asList(am.getAccountsByType(accountType))); + } + // Try to restore them from saved JSON + int restoredCount = 0; + for (final android.accounts.Account amAccount : amAccounts) { + final String jsonString = am.getUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG); + if (TextUtils.isEmpty(jsonString)) { + continue; } - return numRecovered; - } finally { - if (backupDatabase != null) { - backupDatabase.close(); + final Account account = Account.fromJsonString(jsonString); + if (account != null) { + AccountSettingsUtils.commitSettings(context, account); + restoredCount++; } } + return restoredCount; } private static final String MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX = "insert into %s (" @@ -1981,27 +1994,6 @@ public class EmailProvider extends ContentProvider { } result = db.update(tableName, values, selection, selectionArgs); break; - - case ACCOUNT_RESET_NEW_COUNT_ID: - id = uri.getPathSegments().get(1); - ContentValues newMessageCount = CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT; - if (values != null) { - Long set = values.getAsLong(EmailContent.SET_COLUMN_NAME); - if (set != null) { - newMessageCount = new ContentValues(); - newMessageCount.put(AccountColumns.NEW_MESSAGE_COUNT, set); - } - } - result = db.update(tableName, newMessageCount, - whereWithId(id, selection), selectionArgs); - notificationUri = Account.CONTENT_URI; // Only notify account cursors. - break; - case ACCOUNT_RESET_NEW_COUNT: - result = db.update(tableName, CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT, - selection, selectionArgs); - // Affects all accounts. Just invalidate all account cache. - notificationUri = Account.CONTENT_URI; // Only notify account cursors. - break; case MESSAGE_MOVE: result = db.update(MessageMove.TABLE_NAME, values, selection, selectionArgs); break; diff --git a/tests/src/com/android/email/provider/AccountBackupRestoreTests.java b/tests/src/com/android/email/provider/AccountBackupRestoreTests.java index a641c7793..c9b902d5d 100644 --- a/tests/src/com/android/email/provider/AccountBackupRestoreTests.java +++ b/tests/src/com/android/email/provider/AccountBackupRestoreTests.java @@ -67,8 +67,6 @@ public class AccountBackupRestoreTests extends ProviderTestCase2 assertEquals(" mSenderName", expect.mSenderName, actual.mSenderName); assertEquals(" mProtocolVersion", expect.mProtocolVersion, actual.mProtocolVersion); - assertEquals(" mNewMessageCount", expect.mNewMessageCount, - actual.mNewMessageCount); assertEquals(" mSignature", expect.mSignature, actual.mSignature); // Nulled out by backup diff --git a/tests/src/com/android/email/provider/ProviderTestUtils.java b/tests/src/com/android/email/provider/ProviderTestUtils.java index a723a9450..0b8a05fa3 100644 --- a/tests/src/com/android/email/provider/ProviderTestUtils.java +++ b/tests/src/com/android/email/provider/ProviderTestUtils.java @@ -60,7 +60,6 @@ public class ProviderTestUtils extends Assert { account.mFlags = 4; account.mSenderName = name; account.mProtocolVersion = "2.5" + name; - account.mNewMessageCount = 5 + name.length(); account.mPolicyKey = 0; account.mSecuritySyncKey = "sec-sync-key-" + name; account.mSignature = "signature-" + name; @@ -303,8 +302,6 @@ public class ProviderTestUtils extends Assert { assertEquals(caller + " mSenderName", expect.mSenderName, actual.mSenderName); assertEquals(caller + " mProtocolVersion", expect.mProtocolVersion, actual.mProtocolVersion); - assertEquals(caller + " mNewMessageCount", expect.mNewMessageCount, - actual.mNewMessageCount); assertEquals(caller + " mSecuritySyncKey", expect.mSecuritySyncKey, actual.mSecuritySyncKey); assertEquals(caller + " mSignature", expect.mSignature, actual.mSignature); diff --git a/tests/src/com/android/email/provider/ProviderTests.java b/tests/src/com/android/email/provider/ProviderTests.java index 21ffd6175..b7e692290 100644 --- a/tests/src/com/android/email/provider/ProviderTests.java +++ b/tests/src/com/android/email/provider/ProviderTests.java @@ -2044,125 +2044,6 @@ public class ProviderTests extends ProviderTestCase2 { assertEquals(-1, Account.getInboxId(c, 999999)); } - /** - * Check if update to {@link Account#RESET_NEW_MESSAGE_COUNT_URI} resets the new message count. - */ - public void testResetNewMessageCount() { - final Context c = mMockContext; - final ContentResolver cr = c.getContentResolver(); - - // Prepare test data - Account a1 = ProviderTestUtils.setupAccount("acct1", false, c); - a1.mNewMessageCount = 1; - a1.save(c); - Account a2 = ProviderTestUtils.setupAccount("acct2", false, c); - a2.mNewMessageCount = 2; - a2.save(c); - Account a3 = ProviderTestUtils.setupAccount("acct3", false, c); - a3.mNewMessageCount = 3; - a3.save(c); - Account a4 = ProviderTestUtils.setupAccount("acct4", false, c); - a4.mNewMessageCount = 4; - a4.save(c); - Account a5 = ProviderTestUtils.setupAccount("acct5", false, c); - a5.mNewMessageCount = 5; - a5.save(c); - - // With ID in URI, no selection - cr.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a1.mId), null, - null, null); - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - assertEquals(2, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount); - assertEquals(3, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount); - assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount); - assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount); - - // No ID in URI, with selection - cr.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null, EmailContent.ID_SELECTION, - new String[] {Long.toString(a2.mId)}); - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount); - assertEquals(3, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount); - assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount); - assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount); - - // With ID, with selection - cr.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a3.mId), null, - EmailContent.ID_SELECTION, new String[] {Long.toString(a3.mId)}); - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount); - assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount); - assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount); - - // No ID in URI, no selection - cr.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null, null, null); - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount); - assertEquals(0, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount); - } - - /** - * Check if update on ACCOUNT_ID_ADD_TO_FIELD updates the cache properly. - */ - public void testUpdateCacheAccountIdAddToField() { - final Context c = mMockContext; - - // make sure Account.CONTENT_URI is defined - EmailContent.init(c); - Account.initAccount(); - - Account a1 = ProviderTestUtils.setupAccount("a1", true, c); - - int start = Account.restoreAccountWithId(c, a1.mId).mNewMessageCount; - - // +1 to NEW_MESSAGE_COUNT - ContentValues cv = new ContentValues(); - cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT); - cv.put(EmailContent.ADD_COLUMN_NAME, 1); - mProvider.update(ContentUris.withAppendedId(Account.CONTENT_URI, a1.mId), cv, null, null); - - // Check - assertEquals(start + 1, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - } - - /** - * Check if update on ACCOUNT_RESET_NEW_COUNT updates the cache properly. - */ - public void testUpdateCacheAccountResetNewCount() { - final Context c = mMockContext; - Account a1 = ProviderTestUtils.setupAccount("a1", true, c); - - // precondition - assertTrue(Account.restoreAccountWithId(c, a1.mId).mNewMessageCount > 0); - - // Reset - mProvider.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null, null, null); - - // Check - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - } - - /** - * Check if update on ACCOUNT_RESET_NEW_COUNT_ID updates the cache properly. - */ - public void testUpdateCacheAccountResetNewCountId() { - final Context c = mMockContext; - Account a1 = ProviderTestUtils.setupAccount("a1", true, c); - - // precondition - assertTrue(Account.restoreAccountWithId(c, a1.mId).mNewMessageCount > 0); - - // Reset - mProvider.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a1.mId), - null, null, null); - - // Check - assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount); - } - /** * Check that we're handling illegal uri's properly (by throwing an exception unless it's a * query for an id of -1, in which case we return a zero-length cursor)