Synchronize getSyncKey/setSyncKey in Calendar and Contacts sync

* When the sync state of Calendar/Contacts is changed, a number of observer calls
  are triggered.  In addition, we might have a running sync.
* The syncKey operations need to be synchronized, because we may otherwise
  inadvertently use stale data when syncing, which would cause symptoms
  as seen in the referenced bug

Bug: 2561864
Change-Id: I03db58fe01c45778d271fad34d8d4940edefe8fe
This commit is contained in:
Marc Blank 2010-04-01 12:16:29 -07:00
parent 78021cbbf8
commit 79976209d7
3 changed files with 69 additions and 55 deletions

View File

@ -683,7 +683,7 @@ public class SyncManager extends Service implements Runnable {
}
@Override
public void onChange(boolean selfChange) {
public synchronized void onChange(boolean selfChange) {
// See if the user has changed syncing of our calendar
if (!selfChange) {
new Thread(new Runnable() {

View File

@ -105,6 +105,8 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
private static final Uri sExtendedPropertiesUri = asSyncAdapter(ExtendedProperties.CONTENT_URI);
private static final Uri sRemindersUri = asSyncAdapter(Reminders.CONTENT_URI);
private static final Object sSyncKeyLock = new Object();
private long mCalendarId = -1;
private ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
@ -173,20 +175,24 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
*/
@Override
public String getSyncKey() throws IOException {
ContentProviderClient client =
mService.mContentResolver.acquireContentProviderClient(Calendar.CONTENT_URI);
try {
byte[] data = SyncStateContract.Helpers.get(client,
asSyncAdapter(Calendar.SyncState.CONTENT_URI), mAccountManagerAccount);
if (data == null || data.length == 0) {
// Initialize the SyncKey
setSyncKey("0", false);
return "0";
} else {
return new String(data);
synchronized (sSyncKeyLock) {
ContentProviderClient client = mService.mContentResolver
.acquireContentProviderClient(Calendar.CONTENT_URI);
try {
byte[] data = SyncStateContract.Helpers.get(client,
asSyncAdapter(Calendar.SyncState.CONTENT_URI), mAccountManagerAccount);
if (data == null || data.length == 0) {
// Initialize the SyncKey
setSyncKey("0", false);
return "0";
} else {
String syncKey = new String(data);
userLog("SyncKey retrieved as ", syncKey, " from CalendarProvider");
return syncKey;
}
} catch (RemoteException e) {
throw new IOException("Can't get SyncKey from CalendarProvider");
}
} catch (RemoteException e) {
throw new IOException("Can't get SyncKey from ContactsProvider");
}
}
@ -196,19 +202,21 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
*/
@Override
public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
if ("0".equals(syncKey) || !inCommands) {
ContentProviderClient client =
mService.mContentResolver
.acquireContentProviderClient(Calendar.CONTENT_URI);
try {
SyncStateContract.Helpers.set(client, asSyncAdapter(Calendar.SyncState.CONTENT_URI),
mAccountManagerAccount, syncKey.getBytes());
userLog("SyncKey set to ", syncKey, " in CalendarProvider");
} catch (RemoteException e) {
throw new IOException("Can't set SyncKey in CalendarProvider");
synchronized (sSyncKeyLock) {
if ("0".equals(syncKey) || !inCommands) {
ContentProviderClient client = mService.mContentResolver
.acquireContentProviderClient(Calendar.CONTENT_URI);
try {
SyncStateContract.Helpers.set(client,
asSyncAdapter(Calendar.SyncState.CONTENT_URI), mAccountManagerAccount,
syncKey.getBytes());
userLog("SyncKey set to ", syncKey, " in CalendarProvider");
} catch (RemoteException e) {
throw new IOException("Can't set SyncKey in CalendarProvider");
}
}
mMailbox.mSyncKey = syncKey;
}
mMailbox.mSyncKey = syncKey;
}
class EasCalendarSyncParser extends AbstractSyncParser {

View File

@ -118,6 +118,8 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
private static final int[] HOME_PHONE_TAGS = new int[] {Tags.CONTACTS_HOME_TELEPHONE_NUMBER,
Tags.CONTACTS_HOME2_TELEPHONE_NUMBER};
private static final Object sSyncKeyLock = new Object();
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
@ -156,26 +158,29 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
*/
@Override
public String getSyncKey() throws IOException {
ContentProviderClient client =
mService.mContentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
try {
byte[] data = SyncStateContract.Helpers.get(client,
ContactsContract.SyncState.CONTENT_URI, mAccountManagerAccount);
if (data == null || data.length == 0) {
// Initialize the SyncKey
setSyncKey("0", false);
// Make sure ungrouped contacts for Exchange are defaultly visible
ContentValues cv = new ContentValues();
cv.put(Groups.ACCOUNT_NAME, mAccount.mEmailAddress);
cv.put(Groups.ACCOUNT_TYPE, com.android.email.Email.EXCHANGE_ACCOUNT_MANAGER_TYPE);
cv.put(Settings.UNGROUPED_VISIBLE, true);
client.insert(addCallerIsSyncAdapterParameter(Settings.CONTENT_URI), cv);
return "0";
} else {
return new String(data);
synchronized (sSyncKeyLock) {
ContentProviderClient client = mService.mContentResolver
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
try {
byte[] data = SyncStateContract.Helpers.get(client,
ContactsContract.SyncState.CONTENT_URI, mAccountManagerAccount);
if (data == null || data.length == 0) {
// Initialize the SyncKey
setSyncKey("0", false);
// Make sure ungrouped contacts for Exchange are defaultly visible
ContentValues cv = new ContentValues();
cv.put(Groups.ACCOUNT_NAME, mAccount.mEmailAddress);
cv.put(Groups.ACCOUNT_TYPE,
com.android.email.Email.EXCHANGE_ACCOUNT_MANAGER_TYPE);
cv.put(Settings.UNGROUPED_VISIBLE, true);
client.insert(addCallerIsSyncAdapterParameter(Settings.CONTENT_URI), cv);
return "0";
} else {
return new String(data);
}
} catch (RemoteException e) {
throw new IOException("Can't get SyncKey from ContactsProvider");
}
} catch (RemoteException e) {
throw new IOException("Can't get SyncKey from ContactsProvider");
}
}
@ -185,19 +190,20 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
*/
@Override
public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
if ("0".equals(syncKey) || !inCommands) {
ContentProviderClient client =
mService.mContentResolver
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
try {
SyncStateContract.Helpers.set(client, ContactsContract.SyncState.CONTENT_URI,
mAccountManagerAccount, syncKey.getBytes());
userLog("SyncKey set to ", syncKey, " in ContactsProvider");
} catch (RemoteException e) {
throw new IOException("Can't set SyncKey in ContactsProvider");
synchronized (sSyncKeyLock) {
if ("0".equals(syncKey) || !inCommands) {
ContentProviderClient client = mService.mContentResolver
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
try {
SyncStateContract.Helpers.set(client, ContactsContract.SyncState.CONTENT_URI,
mAccountManagerAccount, syncKey.getBytes());
userLog("SyncKey set to ", syncKey, " in ContactsProvider");
} catch (RemoteException e) {
throw new IOException("Can't set SyncKey in ContactsProvider");
}
}
mMailbox.mSyncKey = syncKey;
}
mMailbox.mSyncKey = syncKey;
}
public static final class EasChildren {