From fb084400d6afa6443a421117fbcaee0265d38fb6 Mon Sep 17 00:00:00 2001 From: Fred Quintana Date: Tue, 23 Mar 2010 17:57:03 -0700 Subject: [PATCH] fix bug where sync settings set lost upon upgrade from donut and eclair to froyo - intepret a missing syncavble attribute from donut as "unsynced" rather than the traditional "true" - copy the sync settings from the authorities "contacts" and "calendar" to "com.android.contacts" and "com.android.calendar" if the latter don't already have settings - delay the database cleanup until after boot completed, which will give the GoogleLoginService accounts migration code a chance to run; this was causing all the settings to get removed upon a donut to froyo upgrade Change-Id: I8795e97ba0c9b930d1a50784229ca9ab15dff9d2 http://b/issue?id=2531359 --- core/java/android/content/SyncManager.java | 5 +- .../android/content/SyncStorageEngine.java | 81 ++++++++++++++--- .../content/SyncStorageEngineTest.java | 90 ++++++++++++++++++- 3 files changed, 160 insertions(+), 16 deletions(-) diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index df48f0428c91d..5c8ee187e8d03 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -215,7 +215,9 @@ public class SyncManager implements OnAccountsUpdateListener { // the accounts are not set yet sendCheckAlarmsMessage(); - mSyncStorageEngine.doDatabaseCleanup(accounts); + if (mBootCompleted) { + mSyncStorageEngine.doDatabaseCleanup(accounts); + } if (accounts.length > 0) { // If this is the first time this was called after a bootup then @@ -1317,6 +1319,7 @@ public class SyncManager implements OnAccountsUpdateListener { private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); public void onBootCompleted() { mBootCompleted = true; + mSyncStorageEngine.doDatabaseCleanup(AccountManager.get(mContext).getAccounts()); if (mReadyToRunLatch != null) { mReadyToRunLatch.countDown(); } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 03e606fb4305f..daad95c65ab87 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -122,7 +122,15 @@ public class SyncStorageEngine extends Handler { private static final boolean SYNC_ENABLED_DEFAULT = false; // the version of the accounts xml file format - private static final int ACCOUNTS_VERSION = 1; + private static final int ACCOUNTS_VERSION = 2; + + private static HashMap sAuthorityRenames; + + static { + sAuthorityRenames = new HashMap(); + sAuthorityRenames.put("contacts", "com.android.contacts"); + sAuthorityRenames.put("calendar", "com.android.calendar"); + } public static class PendingOperation { final Account account; @@ -1281,7 +1289,9 @@ public class SyncStorageEngine extends Handler { private void removeAuthorityLocked(Account account, String authorityName) { AccountInfo accountInfo = mAccounts.get(account); if (accountInfo != null) { - if (accountInfo.authorities.remove(authorityName) != null) { + final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName); + if (authorityInfo != null) { + mAuthorities.remove(authorityInfo.ident); writeAccountInfoLocked(); } } @@ -1407,11 +1417,61 @@ public class SyncStorageEngine extends Handler { } } + if (maybeMigrateSettingsForRenamedAuthorities()) { + writeNeeded = true; + } + if (writeNeeded) { writeAccountInfoLocked(); } } + /** + * some authority names have changed. copy over their settings and delete the old ones + * @return true if a change was made + */ + private boolean maybeMigrateSettingsForRenamedAuthorities() { + boolean writeNeeded = false; + + ArrayList authoritiesToRemove = new ArrayList(); + final int N = mAuthorities.size(); + for (int i=0; i= 0) { + String authorityName = parser.getAttributeValue(null, "authority"); + String enabled = parser.getAttributeValue(null, "enabled"); + String syncable = parser.getAttributeValue(null, "syncable"); String accountName = parser.getAttributeValue(null, "account"); String accountType = parser.getAttributeValue(null, "type"); if (accountType == null) { accountType = "com.google"; + syncable = "unknown"; } - String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, "enabled"); - String syncable = parser.getAttributeValue(null, "syncable"); authority = mAuthorities.get(id); if (DEBUG_FILE) Log.v(TAG, "Adding authority: account=" + accountName + " auth=" + authorityName @@ -1456,7 +1517,7 @@ public class SyncStorageEngine extends Handler { authority.syncable = -1; } else { authority.syncable = - (syncable == null || Boolean.parseBoolean(enabled)) ? 1 : 0; + (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0; } } else { Log.w(TAG, "Failure adding authority: account=" @@ -1546,13 +1607,11 @@ public class SyncStorageEngine extends Handler { out.attribute(null, "account", authority.account.name); out.attribute(null, "type", authority.account.type); out.attribute(null, "authority", authority.authority); - if (!authority.enabled) { - out.attribute(null, "enabled", "false"); - } + out.attribute(null, "enabled", Boolean.toString(authority.enabled)); if (authority.syncable < 0) { out.attribute(null, "syncable", "unknown"); - } else if (authority.syncable == 0) { - out.attribute(null, "syncable", "false"); + } else { + out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0)); } for (Pair periodicSync : authority.periodicSyncs) { out.startTag(null, "periodicSync"); diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java index 7028d1abc951a..48fe765e1ca65 100644 --- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java +++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java @@ -214,7 +214,6 @@ public class SyncStorageEngineTest extends AndroidTestCase { MockContentResolver mockResolver = new MockContentResolver(); final TestContext testContext = new TestContext(mockResolver, getContext()); - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); byte[] accountsFileData = ("\n" + "\n" @@ -230,7 +229,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { fos.write(accountsFileData); accountInfoFile.finishWrite(fos); - engine.clearAndReadState(); + SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); List syncs = engine.getPeriodicSyncs(account, authority1); assertEquals(1, syncs.size()); @@ -245,7 +244,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(sync3, syncs.get(0)); accountsFileData = ("\n" - + "\n" + + "\n" + "\n" + "\n" + "\n" @@ -268,7 +267,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(0, syncs.size()); accountsFileData = ("\n" - + "\n" + + "\n" + "\n" + "\n" + "" @@ -299,6 +298,89 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(1, syncs.size()); assertEquals(sync3s, syncs.get(0)); } + + @SmallTest + public void testAuthorityRenaming() throws Exception { + final Account account1 = new Account("acc1", "type1"); + final Account account2 = new Account("acc2", "type2"); + final String authorityContacts = "contacts"; + final String authorityCalendar = "calendar"; + final String authorityOther = "other"; + final String authorityContactsNew = "com.android.contacts"; + final String authorityCalendarNew = "com.android.calendar"; + + MockContentResolver mockResolver = new MockContentResolver(); + + final TestContext testContext = new TestContext(mockResolver, getContext()); + + byte[] accountsFileData = ("\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n").getBytes(); + + File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); + syncDir.mkdirs(); + AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); + FileOutputStream fos = accountInfoFile.startWrite(); + fos.write(accountsFileData); + accountInfoFile.finishWrite(fos); + + SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); + + assertEquals(false, engine.getSyncAutomatically(account1, authorityContacts)); + assertEquals(false, engine.getSyncAutomatically(account1, authorityCalendar)); + assertEquals(true, engine.getSyncAutomatically(account1, authorityOther)); + assertEquals(true, engine.getSyncAutomatically(account1, authorityContactsNew)); + assertEquals(true, engine.getSyncAutomatically(account1, authorityCalendarNew)); + + assertEquals(false, engine.getSyncAutomatically(account2, authorityContacts)); + assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendar)); + assertEquals(true, engine.getSyncAutomatically(account2, authorityOther)); + assertEquals(false, engine.getSyncAutomatically(account2, authorityContactsNew)); + assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendarNew)); + } + + @SmallTest + public void testSyncableMigration() throws Exception { + final Account account = new Account("acc", "type"); + + MockContentResolver mockResolver = new MockContentResolver(); + + final TestContext testContext = new TestContext(mockResolver, getContext()); + + byte[] accountsFileData = ("\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n").getBytes(); + + File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); + syncDir.mkdirs(); + AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); + FileOutputStream fos = accountInfoFile.startWrite(); + fos.write(accountsFileData); + accountInfoFile.finishWrite(fos); + + SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); + + assertEquals(-1, engine.getIsSyncable(account, "other1")); + assertEquals(1, engine.getIsSyncable(account, "other2")); + assertEquals(0, engine.getIsSyncable(account, "other3")); + assertEquals(1, engine.getIsSyncable(account, "other4")); + } } class TestContext extends ContextWrapper {