From 60a74318163968ce3db46c1952ed7bc4f8ce530e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=8C=AF=E6=B7=A6=E7=8E=8B?= Date: Tue, 1 Dec 2015 16:37:31 +0800 Subject: [PATCH] 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 --- .../android/server/content/SyncManager.java | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 334bc18b82f28..25192bf355a52 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -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);