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
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index f3d3902..043411b 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -683,7 +683,7 @@
}
@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() {
diff --git a/src/com/android/exchange/adapter/CalendarSyncAdapter.java b/src/com/android/exchange/adapter/CalendarSyncAdapter.java
index df0296b..a25f83b 100644
--- a/src/com/android/exchange/adapter/CalendarSyncAdapter.java
+++ b/src/com/android/exchange/adapter/CalendarSyncAdapter.java
@@ -105,6 +105,8 @@
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 @@
*/
@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 @@
*/
@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 {
diff --git a/src/com/android/exchange/adapter/ContactsSyncAdapter.java b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
index 1b9b1f7..8b4adba 100644
--- a/src/com/android/exchange/adapter/ContactsSyncAdapter.java
+++ b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
@@ -118,6 +118,8 @@
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 @@
*/
@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 @@
*/
@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 {