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
This commit is contained in:
Fred Quintana
2010-03-23 17:57:03 -07:00
parent 49de491379
commit fb084400d6
3 changed files with 160 additions and 16 deletions

View File

@@ -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();
}

View File

@@ -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<String, String> sAuthorityRenames;
static {
sAuthorityRenames = new HashMap<String, String>();
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<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
final int N = mAuthorities.size();
for (int i=0; i<N; i++) {
AuthorityInfo authority = mAuthorities.valueAt(i);
// skip this authority if it isn't one of the renamed ones
final String newAuthorityName = sAuthorityRenames.get(authority.authority);
if (newAuthorityName == null) {
continue;
}
// remember this authority so we can remove it later. we can't remove it
// now without messing up this loop iteration
authoritiesToRemove.add(authority);
// this authority isn't enabled, no need to copy it to the new authority name since
// the default is "disabled"
if (!authority.enabled) {
continue;
}
// if we already have a record of this new authority then don't copy over the settings
if (getAuthorityLocked(authority.account, newAuthorityName, "cleanup") != null) {
continue;
}
AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
newAuthorityName, -1 /* ident */, false /* doWrite */);
newAuthority.enabled = true;
writeNeeded = true;
}
for (AuthorityInfo authorityInfo : authoritiesToRemove) {
removeAuthorityLocked(authorityInfo.account, authorityInfo.authority);
writeNeeded = true;
}
return writeNeeded;
}
private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
AuthorityInfo authority = null;
int id = -1;
@@ -1424,14 +1484,15 @@ public class SyncStorageEngine extends Handler {
Log.e(TAG, "the id of the authority is null", e);
}
if (id >= 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<Bundle, Long> periodicSync : authority.periodicSyncs) {
out.startTag(null, "periodicSync");

View File

@@ -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 = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts>\n"
@@ -230,7 +229,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
fos.write(accountsFileData);
accountInfoFile.finishWrite(fos);
engine.clearAndReadState();
SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority1);
assertEquals(1, syncs.size());
@@ -245,7 +244,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
assertEquals(sync3, syncs.get(0));
accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts version=\"1\">\n"
+ "<accounts version=\"2\">\n"
+ "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
+ "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
+ "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
@@ -268,7 +267,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
assertEquals(0, syncs.size());
accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts version=\"1\">\n"
+ "<accounts version=\"2\">\n"
+ "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
+ "<periodicSync period=\"1000\" />\n"
+ "</authority>"
@@ -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 = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts>\n"
+ "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n"
+ "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n"
+ "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n"
+ "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n"
+ "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n"
+ "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n"
+ "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\""
+ " authority=\"com.android.calendar\" />\n"
+ "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\""
+ " authority=\"com.android.contacts\" />\n"
+ "</accounts>\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 = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts>\n"
+ "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n"
+ "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n"
+ "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\""
+ " authority=\"other3\" />\n"
+ "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\""
+ " authority=\"other4\" />\n"
+ "</accounts>\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 {