From 067c874c316f36b80551dbda2280e1a4f964eafe Mon Sep 17 00:00:00 2001 From: Andy Stadler Date: Mon, 28 Feb 2011 15:24:05 -0800 Subject: [PATCH] DO NOT MERGE: Backport fix for IMAP delete bug Original CL comment: Always set a delete policy for legacy accounts The delete policy can only be set for POP3 accounts. However, the delete policy is used for all legacy accounts (that includes IMAP). As such, we need to make sure IMAP accounts also have their policy set; even though the setting is not configurable by the user. The delete policy does not mean anything for Exchange accounts, so, we do not need to modify the account setup code for them. bug 3074164 Original Change-Id: Iab10d2997404b3b0c10a60a64fb652540c0d2d1a Change-Id: Idc290aa1b8ff4f17a0c8fd57333523becef0c8e5 --- .../setup/AccountSetupIncomingFragment.java | 15 +- .../EmailBroadcastProcessorService.java | 43 ++++++ .../EmailBroadcastProcessorServiceTests.java | 138 ++++++++++++++++++ 3 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 tests/src/com/android/email/service/EmailBroadcastProcessorServiceTests.java diff --git a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java index d7e038fa3..5c8884a5c 100644 --- a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java +++ b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java @@ -20,6 +20,7 @@ import com.android.email.AccountBackupRestore; import com.android.email.Email; import com.android.email.R; import com.android.email.Utility; +import com.android.email.mail.Store; import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent.Account; @@ -334,17 +335,21 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment { mPasswordView.setText(password); } - if (uri.getScheme().startsWith("pop3")) { - mLoadedDeletePolicy = account.getDeletePolicy(); - SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mLoadedDeletePolicy); - } else if (uri.getScheme().startsWith("imap")) { + if (uri.getScheme().startsWith(Store.STORE_SCHEME_IMAP)) { if (uri.getPath() != null && uri.getPath().length() > 0) { mImapPathPrefixView.setText(uri.getPath().substring(1)); } - } else { + } else if (!uri.getScheme().startsWith(Store.STORE_SCHEME_POP3)) { + // Account must either be IMAP or POP3 throw new Error("Unknown account type: " + account.getStoreUri(mContext)); } + // The delete policy is set for all accounts. For POP3 accounts, the user sets + // the policy explicitly. For IMAP accounts, the policy is set when the Account object + // is created. @see AccountSetupBasics#populateSetupData + mLoadedDeletePolicy = account.getDeletePolicy(); + SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mLoadedDeletePolicy); + for (int i = 0; i < mAccountSchemes.length; i++) { if (mAccountSchemes[i].equals(uri.getScheme())) { SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, i); diff --git a/src/com/android/email/service/EmailBroadcastProcessorService.java b/src/com/android/email/service/EmailBroadcastProcessorService.java index ea930922e..a70694766 100644 --- a/src/com/android/email/service/EmailBroadcastProcessorService.java +++ b/src/com/android/email/service/EmailBroadcastProcessorService.java @@ -22,14 +22,23 @@ import com.android.email.Preferences; import com.android.email.SecurityPolicy; import com.android.email.VendorPolicyLoader; import com.android.email.activity.setup.AccountSettingsXL; +import com.android.email.mail.Store; +import com.android.email.provider.EmailContent.Account; +import com.android.email.provider.EmailContent.AccountColumns; +import com.android.email.provider.EmailContent.HostAuth; import com.android.email.provider.WidgetProvider; import android.accounts.AccountManager; import android.app.IntentService; import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; import android.util.Log; /** @@ -155,6 +164,12 @@ public class EmailBroadcastProcessorService extends IntentService { ExchangeUtils.enableEasCalendarSync(this); } + if (progress < 2) { + Log.i(Email.LOG_TAG, "Onetime initialization: 2"); + progress = 2; + setImapDeletePolicy(this); + } + // Add your initialization steps here. // Use "progress" to skip the initializations that's already done before. // Using this preference also makes it safe when a user skips an upgrade. (i.e. upgrading @@ -166,6 +181,34 @@ public class EmailBroadcastProcessorService extends IntentService { } } + /** + * Sets the delete policy to the correct value for all IMAP accounts. This will have no + * effect on either EAS or POP3 accounts. + */ + /*package*/ static void setImapDeletePolicy(Context context) { + ContentResolver resolver = context.getContentResolver(); + Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, + null, null, null); + try { + while (c.moveToNext()) { + long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN); + HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey); + if (Store.STORE_SCHEME_IMAP.equals(recvAuth.mProtocol)) { + int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN); + flags &= ~Account.FLAGS_DELETE_POLICY_MASK; + flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT; + ContentValues cv = new ContentValues(); + cv.put(AccountColumns.FLAGS, flags); + long accountId = c.getLong(Account.CONTENT_ID_COLUMN); + Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); + resolver.update(uri, cv, null, null); + } + } + } finally { + c.close(); + } + } + private void setComponentEnabled(Class clazz, boolean enabled) { final ComponentName c = new ComponentName(this, clazz.getName()); getPackageManager().setComponentEnabledSetting(c, diff --git a/tests/src/com/android/email/service/EmailBroadcastProcessorServiceTests.java b/tests/src/com/android/email/service/EmailBroadcastProcessorServiceTests.java new file mode 100644 index 000000000..d68915151 --- /dev/null +++ b/tests/src/com/android/email/service/EmailBroadcastProcessorServiceTests.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.email.service; + +import com.android.email.AccountTestCase; +import com.android.email.Utility; +import com.android.email.provider.EmailContent.Account; +import com.android.email.provider.EmailContent.AccountColumns; +import com.android.email.provider.EmailContent.HostAuth; +import com.android.email.provider.ProviderTestUtils; + +import android.content.ContentUris; +import android.content.Context; +import android.net.Uri; + +import java.util.NoSuchElementException; + +/** + * Tests of the Email provider. + * + * You can run this entire test case with: + * runtest -c com.android.email.service.EmailBroadcastProcessorServiceTests email + */ +public class EmailBroadcastProcessorServiceTests extends AccountTestCase { + + Context mMockContext; + + public EmailBroadcastProcessorServiceTests() { + super(); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + mMockContext = getMockContext(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Create a simple HostAuth with protocol + */ + private HostAuth setupSimpleHostAuth(String protocol) { + HostAuth hostAuth = ProviderTestUtils.setupHostAuth(protocol, "name", 1L, false, mContext); + hostAuth.mProtocol = protocol; + return hostAuth; + } + + /** + * Returns the flags for the specified account. Throws an exception if the account cannot + * be found. + */ + private int getAccountFlags(long accountId) throws NoSuchElementException { + Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); + Integer flags = Utility.getFirstRowInt(mMockContext, uri, + new String[] { AccountColumns.FLAGS }, null, null, null, 0); + if (flags == null) { + throw new NoSuchElementException("No cursor"); + } + return flags; + } + + /** + * Initial testing on setupSyncReportsLocked, making sure that EAS accounts aren't scheduled + */ + public void testSetImapDeletePolicy() { + // Setup accounts of each type, all with manual sync at different intervals + Account account1 = ProviderTestUtils.setupAccount("eas-account1", false, mMockContext); + account1.mHostAuthRecv = setupSimpleHostAuth("eas"); + account1.mHostAuthSend = account1.mHostAuthRecv; + account1.save(mMockContext); + long accountId1 = account1.mId; + Account account2 = ProviderTestUtils.setupAccount("pop-account1", false, mMockContext); + account2.mHostAuthRecv = setupSimpleHostAuth("pop3"); + account2.mHostAuthSend = setupSimpleHostAuth("smtp"); + account2.mFlags = 0x08; // set delete policy + account2.save(mMockContext); + long accountId2 = account2.mId; + Account account3 = ProviderTestUtils.setupAccount("pop-account2", false, mMockContext); + account3.mHostAuthRecv = setupSimpleHostAuth("pop3"); + account3.mHostAuthSend = setupSimpleHostAuth("smtp"); + account3.save(mMockContext); + long accountId3 = account3.mId; + Account account4 = ProviderTestUtils.setupAccount("imap-account1", false, mMockContext); + account4.mHostAuthRecv = setupSimpleHostAuth("imap"); + account4.mHostAuthSend = setupSimpleHostAuth("smtp"); + account4.mFlags = 0xa5a5a5a5; // Alternating bits; includes bad delete policy + account4.save(mMockContext); + long accountId4 = account4.mId; + Account account5 = ProviderTestUtils.setupAccount("imap-account2", false, mMockContext); + account5.mHostAuthRecv = setupSimpleHostAuth("imap"); + account5.mHostAuthSend = setupSimpleHostAuth("smtp"); + account5.mFlags = 0x0c; // All delete policy bits set + account5.save(mMockContext); + long accountId5 = account5.mId; + Account account6 = ProviderTestUtils.setupAccount("imap-account3", false, mMockContext); + account6.mHostAuthRecv = setupSimpleHostAuth("imap"); + account6.mHostAuthSend = setupSimpleHostAuth("smtp"); + account6.mFlags = 0; // No delete policy bits set + account6.save(mMockContext); + long accountId6 = account6.mId; + + // Run the account migration + EmailBroadcastProcessorService.setImapDeletePolicy(mMockContext); + + // Test the results + int accountFlags1 = getAccountFlags(accountId1); + assertEquals(4, accountFlags1); // not IMAP; no changes + int accountFlags2 = getAccountFlags(accountId2); + assertEquals(8, accountFlags2); // not IMAP; no changes + int accountFlags3 = getAccountFlags(accountId3); + assertEquals(4, accountFlags3); // not IMAP; no changes + int accountFlags4 = getAccountFlags(accountId4); + assertEquals(0xa5a5a5a9, accountFlags4); // Only update delete policy bits + int accountFlags5 = getAccountFlags(accountId5); + assertEquals(0x00000008, accountFlags5); + int accountFlags6 = getAccountFlags(accountId6); + assertEquals(0x00000008, accountFlags6); + } + +}