Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60a74318 authored by 振淦王's avatar 振淦王 Committed by 振淦 王
Browse files

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: default avatarwangzhengan <wangzglx@gmail.com>
parent 7d72975c
Loading
Loading
Loading
Loading
+33 −19
Original line number Original line Diff line number Diff line
@@ -315,25 +315,9 @@ public class SyncManager {
    }
    }


    public void updateRunningAccounts() {
    public void updateRunningAccounts() {
        mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");

        // Update accounts in handler thread.
        if (mBootCompleted) {
        mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
            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 void doDatabaseCleanup() {
    private void doDatabaseCleanup() {
@@ -2098,6 +2082,7 @@ public class SyncManager {
         * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
         * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
         */
         */
        private static final int MESSAGE_MONITOR_SYNC = 8;
        private static final int MESSAGE_MONITOR_SYNC = 8;
        private static final int MESSAGE_ACCOUNTS_UPDATED = 9;


        public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
        public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
        private Long mAlarmScheduleTime = null;
        private Long mAlarmScheduleTime = null;
@@ -2215,6 +2200,13 @@ public class SyncManager {
                // to also take into account the periodic syncs.
                // to also take into account the periodic syncs.
                earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                switch (msg.what) {
                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:
                    case SyncHandler.MESSAGE_CANCEL:
                        SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                        SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                        Bundle extras = msg.peekData();
                        Bundle extras = msg.peekData();
@@ -2763,6 +2755,28 @@ public class SyncManager {
            return nextReadyToRunTime;
            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) {
        private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
            final long bytesTransferredCurrent =
            final long bytesTransferredCurrent =
                    getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
                    getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);