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:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user