ConcurrentModificationException in SyncManager

updateRunningAccounts and dispatchSyncOperation is running in different thread,
and mActiveSyncContexts not synchronization.
If methods occur together,
mActiveSyncContexts change may occur when cycle is running.

Sample:
https://code.google.com/p/android/issues/detail?id=195341

Solution:
Offboard the traversal from the main thread to the handler thread.

Change-Id: Ifb9fbb466a5087186be31f7877c50b8c8b6737e4
Signed-off-by: wangzhengan <wangzglx@gmail.com>
This commit is contained in:
振淦王
2015-12-01 16:37:31 +08:00
committed by 振淦 王
parent 7d72975c5b
commit 60a7431816

View File

@@ -315,25 +315,9 @@ public class SyncManager {
}
public void updateRunningAccounts() {
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
if (mBootCompleted) {
doDatabaseCleanup();
}
AccountAndUser[] accounts = mRunningAccounts;
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
if (!containsAccountAndUser(accounts,
currentSyncContext.mSyncOperation.target.account,
currentSyncContext.mSyncOperation.target.userId)) {
Log.d(TAG, "canceling sync since the account is no longer running");
sendSyncFinishedOrCanceledMessage(currentSyncContext,
null /* no result since this is a cancel */);
}
}
// we must do this since we don't bother scheduling alarms when
// the accounts are not set yet
sendCheckAlarmsMessage();
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
// Update accounts in handler thread.
mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
}
private void doDatabaseCleanup() {
@@ -2098,6 +2082,7 @@ public class SyncManager {
* obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
*/
private static final int MESSAGE_MONITOR_SYNC = 8;
private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
private Long mAlarmScheduleTime = null;
@@ -2215,6 +2200,13 @@ public class SyncManager {
// to also take into account the periodic syncs.
earliestFuturePollTime = scheduleReadyPeriodicSyncs();
switch (msg.what) {
case SyncHandler.MESSAGE_ACCOUNTS_UPDATED:
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
}
updateRunningAccountsH();
break;
case SyncHandler.MESSAGE_CANCEL:
SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
Bundle extras = msg.peekData();
@@ -2763,6 +2755,28 @@ public class SyncManager {
return nextReadyToRunTime;
}
private void updateRunningAccountsH() {
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
if (mBootCompleted) {
doDatabaseCleanup();
}
AccountAndUser[] accounts = mRunningAccounts;
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
if (!containsAccountAndUser(accounts,
currentSyncContext.mSyncOperation.target.account,
currentSyncContext.mSyncOperation.target.userId)) {
Log.d(TAG, "canceling sync since the account is no longer running");
sendSyncFinishedOrCanceledMessage(currentSyncContext,
null /* no result since this is a cancel */);
}
}
// we must do this since we don't bother scheduling alarms when
// the accounts are not set yet
sendCheckAlarmsMessage();
}
private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
final long bytesTransferredCurrent =
getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);