Fix various problems related to synchronization, reload folder list, etc.
* The reloadFolderList service call mustn't reset the account mailbox if the foler
list has never synced
* Be careful to not commit anything after a mailbox has been stopped
* Be careful to synchronize before checking the stopped state of a mailbox
diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java
index 12dd3b0..063fb53 100644
--- a/src/com/android/exchange/AbstractSyncService.java
+++ b/src/com/android/exchange/AbstractSyncService.java
@@ -70,6 +70,8 @@
protected Context mContext;
public int mChangeCount = 0;
public int mSyncReason = 0;
+ protected volatile boolean mStop = false;
+ private Object mSynchronizer = new Object();
protected volatile long mRequestTime = 0;
protected ArrayList<PartRequest> mPartRequests = new ArrayList<PartRequest>();
@@ -189,6 +191,14 @@
}
}
+ public boolean isStopped() {
+ return mStop;
+ }
+
+ public Object getSynchronizer() {
+ return mSynchronizer;
+ }
+
/**
* Asks SyncManager for a WaitLock for this sync
*/
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index e11da64..2a22b41 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -80,7 +80,8 @@
public class EasSyncService extends AbstractSyncService {
- private static final String WINDOW_SIZE = "10";
+ private static final String EMAIL_WINDOW_SIZE = "5";
+ private static final String PIM_WINDOW_SIZE = "20";
private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
private static final String WHERE_SYNC_INTERVAL_PING =
@@ -112,8 +113,6 @@
public ContentResolver mContentResolver;
String[] mBindArguments = new String[2];
InputStream mPendingPartInputStream = null;
- private volatile boolean mStop = false;
- private Object mWaitTarget = new Object();
private boolean mTriedReloadFolderList = false;
public EasSyncService(Context _context, Mailbox _mailbox) {
@@ -135,8 +134,9 @@
@Override
public void ping() {
userLog("We've been pinged!");
- synchronized (mWaitTarget) {
- mWaitTarget.notify();
+ Object synchronizer = getSynchronizer();
+ synchronized (synchronizer) {
+ synchronizer.notify();
}
}
@@ -426,12 +426,16 @@
if (mAccount.mSyncKey == null) {
mAccount.mSyncKey = "0";
- userLog("Account syncKey RESET");
+ userLog("Account syncKey INIT to 0");
ContentValues cv = new ContentValues();
cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
mAccount.update(mContext, cv);
}
+ if (mAccount.mSyncKey.equals("0")) {
+ userLog("Initial FolderSync");
+ }
+
// When we first start up, change all ping mailboxes to push.
ContentValues cv = new ContentValues();
cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
@@ -440,9 +444,9 @@
SyncManager.kick();
}
- userLog("Account syncKey: " + mAccount.mSyncKey);
// Determine our protocol version, if we haven't already
if (mAccount.mProtocolVersion == null) {
+ userLog("Determine EAS protocol version");
HttpURLConnection uc = setupEASCommand("OPTIONS", null);
if (uc != null) {
int code = uc.getResponseCode();
@@ -467,12 +471,15 @@
}
}
}
+
while (!mStop) {
+ userLog("Sending Account syncKey: " + mAccount.mSyncKey);
Serializer s = new Serializer();
s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
.text(mAccount.mSyncKey).end().end().done();
HttpURLConnection uc = sendEASPostCommand("FolderSync", s.toString());
int code = uc.getResponseCode();
+ if (mStop) break;
if (code == HttpURLConnection.HTTP_OK) {
String encoding = uc.getHeaderField("Transfer-Encoding");
if (encoding == null) {
@@ -895,7 +902,8 @@
if (!mailbox.mSyncKey.equals("0")) {
s.tag(Tags.SYNC_GET_CHANGES);
}
- s.data(Tags.SYNC_WINDOW_SIZE, WINDOW_SIZE);
+ s.data(Tags.SYNC_WINDOW_SIZE,
+ className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
boolean options = false;
if (!className.equals("Contacts")) {
// Set the lookback appropriately (EAS calls this a "filter")
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index fa074af..4a73154 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -291,14 +291,14 @@
for (Account account : mAccounts) {
if (!currentAccounts.contains(account.mId)) {
// This is a deletion; shut down any account-related syncs
- stopAccountSyncs(account.mId);
+ stopAccountSyncs(account.mId, true);
} else {
// See whether any of our accounts has changed sync interval or window
if (accountChanged(account)) {
// Here's one that has...
INSTANCE.log("Account " + account.mDisplayName +
" changed; stopping running syncs...");
- stopAccountSyncs(account.mId);
+ stopAccountSyncs(account.mId, false);
}
}
}
@@ -343,13 +343,17 @@
INSTANCE.log("Initializing account: " + acct.mDisplayName);
}
- void stopAccountSyncs(long acctId) {
+ void stopAccountSyncs(long acctId, boolean includeAccountMailbox) {
synchronized (mSyncToken) {
List<Long> deletedBoxes = new ArrayList<Long>();
for (Long mid : INSTANCE.mServiceMap.keySet()) {
Mailbox box = Mailbox.restoreMailboxWithId(INSTANCE, mid);
if (box != null) {
if (box.mAccountKey == acctId) {
+ if (!includeAccountMailbox &&
+ box.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
+ continue;
+ }
AbstractSyncService svc = INSTANCE.mServiceMap.get(mid);
if (svc != null) {
svc.stop();
@@ -498,11 +502,19 @@
try {
if (c.moveToFirst()) {
synchronized(mSyncToken) {
- long id = c.getLong(Mailbox.CONTENT_ID_COLUMN);
+ Mailbox m = new Mailbox().restore(c);
+ String syncKey = m.mSyncKey;
+ // No need to reload the list if we don't have one
+ if (syncKey == null || syncKey.equals("0")) {
+ return;
+ }
+ long id = m.mId;
AbstractSyncService svc = INSTANCE.mServiceMap.get(id);
// Tell the service we're done
if (svc != null) {
- svc.stop();
+ synchronized (svc.getSynchronizer()) {
+ svc.stop();
+ }
// Interrupt the thread so that it can stop
Thread thread = svc.mThread;
thread.setName(thread.getName() + " (Stopped)");
@@ -529,7 +541,7 @@
static public void folderListReloaded(long acctId) {
if (INSTANCE != null) {
AccountObserver obs = INSTANCE.mAccountObserver;
- obs.stopAccountSyncs(acctId);
+ obs.stopAccountSyncs(acctId, false);
obs.addAccountMailbox(acctId);
}
}
@@ -740,7 +752,6 @@
// If we're really debugging, turn on all logging
if (Eas.DEBUG) {
- Eas.PARSER_LOG = true;
Eas.USER_LOG = true;
}
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
index c337407..6d18e02 100644
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ b/src/com/android/exchange/adapter/AbstractSyncParser.java
@@ -135,12 +135,16 @@
}
}
- // Make sure we save away the new syncKey, syncFrequency, etc.
- ContentValues cv = new ContentValues();
- cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
- cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
- mMailbox.update(mContext, cv);
- mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
+ synchronized (mService.getSynchronizer()) {
+ if (!mService.isStopped()) {
+ // Make sure we save away the new syncKey, syncFrequency, etc.
+ ContentValues cv = new ContentValues();
+ cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
+ cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
+ mMailbox.update(mContext, cv);
+ mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
+ }
+ }
// Let the caller know that there's more to do
return moreAvailable;
}
diff --git a/src/com/android/exchange/adapter/ContactsSyncAdapter.java b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
index 7cd2882..6930b88 100644
--- a/src/com/android/exchange/adapter/ContactsSyncAdapter.java
+++ b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
@@ -760,16 +760,20 @@
}
public void execute() {
- try {
- mService.userLog("Executing " + size() + " CPO's");
- mResults = mService.mContext.getContentResolver()
- .applyBatch(ContactsContract.AUTHORITY, this);
- } catch (RemoteException e) {
- // There is nothing sensible to be done here
- Log.e(TAG, "problem inserting contact during server update", e);
- } catch (OperationApplicationException e) {
- // There is nothing sensible to be done here
- Log.e(TAG, "problem inserting contact during server update", e);
+ synchronized (mService.getSynchronizer()) {
+ if (!mService.isStopped()) {
+ try {
+ mService.userLog("Executing " + size() + " CPO's");
+ mResults = mService.mContext.getContentResolver().applyBatch(
+ ContactsContract.AUTHORITY, this);
+ } catch (RemoteException e) {
+ // There is nothing sensible to be done here
+ Log.e(TAG, "problem inserting contact during server update", e);
+ } catch (OperationApplicationException e) {
+ // There is nothing sensible to be done here
+ Log.e(TAG, "problem inserting contact during server update", e);
+ }
+ }
}
}
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index 9d07066..399bb8d 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -437,13 +437,17 @@
addCleanupOps(ops);
- try {
- mService.mContext.getContentResolver()
- .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
- } catch (RemoteException e) {
- // There is nothing to be done here; fail by returning null
- } catch (OperationApplicationException e) {
- // There is nothing to be done here; fail by returning null
+ // No commits if we're stopped
+ synchronized (mService.getSynchronizer()) {
+ if (mService.isStopped()) return;
+ try {
+ mService.mContext.getContentResolver()
+ .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
+ } catch (RemoteException e) {
+ // There is nothing to be done here; fail by returning null
+ } catch (OperationApplicationException e) {
+ // There is nothing to be done here; fail by returning null
+ }
}
mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 72fbe56..89d24c6 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -137,9 +137,15 @@
} else
skipTag();
}
- ContentValues cv = new ContentValues();
- cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
- mAccount.update(mContext, cv);
+ synchronized (mService.getSynchronizer()) {
+ if (!mService.isStopped()) {
+ ContentValues cv = new ContentValues();
+ cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
+ mAccount.update(mContext, cv);
+ mService.userLog("Leaving FolderSyncParser with Account syncKey="
+ + mAccount.mSyncKey);
+ }
+ }
return res;
}
@@ -281,47 +287,50 @@
}
// Create the new mailboxes in a single batch operation
- if (!ops.isEmpty()) {
- mService.userLog("Applying " + ops.size() + " mailbox operations.");
+ // Don't save any data if the service has been stopped
+ synchronized (mService.getSynchronizer()) {
+ if (!ops.isEmpty() && !mService.isStopped()) {
+ mService.userLog("Applying " + ops.size() + " mailbox operations.");
- // Then, we create an update for the account (most importantly, updating the syncKey)
- ops.add(ContentProviderOperation.newUpdate(
- ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId)).withValues(
- mAccount.toContentValues()).build());
+ // Then, we create an update for the account (most importantly, updating the syncKey)
+ ops.add(ContentProviderOperation.newUpdate(
+ ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId)).withValues(
+ mAccount.toContentValues()).build());
- // Finally, we execute the batch
- try {
- mService.mContext.getContentResolver()
- .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
- mService.userLog("New Account SyncKey: " + mAccount.mSyncKey);
- } catch (RemoteException e) {
- // There is nothing to be done here; fail by returning null
- } catch (OperationApplicationException e) {
- // There is nothing to be done here; fail by returning null
- }
-
- // Look for sync issues and its children and delete them
- // I'm not aware of any other way to deal with this properly
- mBindArguments[0] = "Sync Issues";
- mBindArguments[1] = mAccountIdAsString;
- Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_ID_COLUMNS_PROJECTION,
- WHERE_DISPLAY_NAME_AND_ACCOUNT, mBindArguments, null);
- String parentServerId = null;
- long id = 0;
- try {
- if (c.moveToFirst()) {
- id = c.getLong(0);
- parentServerId = c.getString(1);
+ // Finally, we execute the batch
+ try {
+ mService.mContext.getContentResolver()
+ .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
+ mService.userLog("New Account SyncKey: " + mAccount.mSyncKey);
+ } catch (RemoteException e) {
+ // There is nothing to be done here; fail by returning null
+ } catch (OperationApplicationException e) {
+ // There is nothing to be done here; fail by returning null
}
- } finally {
- c.close();
- }
- if (parentServerId != null) {
- mContentResolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id),
- null, null);
- mBindArguments[0] = parentServerId;
- mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
- mBindArguments);
+
+ // Look for sync issues and its children and delete them
+ // I'm not aware of any other way to deal with this properly
+ mBindArguments[0] = "Sync Issues";
+ mBindArguments[1] = mAccountIdAsString;
+ Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_ID_COLUMNS_PROJECTION,
+ WHERE_DISPLAY_NAME_AND_ACCOUNT, mBindArguments, null);
+ String parentServerId = null;
+ long id = 0;
+ try {
+ if (c.moveToFirst()) {
+ id = c.getLong(0);
+ parentServerId = c.getString(1);
+ }
+ } finally {
+ c.close();
+ }
+ if (parentServerId != null) {
+ mContentResolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id),
+ null, null);
+ mBindArguments[0] = parentServerId;
+ mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
+ mBindArguments);
+ }
}
}
}