diff options
author | Jay Shrauner <shrauner@google.com> | 2014-08-14 13:30:28 -0700 |
---|---|---|
committer | Jay Shrauner <shrauner@google.com> | 2014-08-15 13:41:54 -0700 |
commit | cbbbfcba0c68498cde93da2e9b10e748cc5e91ef (patch) | |
tree | b9fcca121814ba3be7a435ed20e18e285b42e554 | |
parent | a920437f6af7b49669518d6e145ca444c6870560 (diff) |
Prevent ConcurrentModificationExceptions
Use sets backed by ConcurrentHashMaps instead of HashSets, and
CopyOnWriteArrayLists instead of ArrayLists, to prevent concurrent
exceptions if listeners try to remove themselves in callbacks while
iterating over the listeners.
Bug:16325026
Change-Id: Ia86f5cafa1d844aa7927df8ff9b02b5574bdde2b
3 files changed, 37 insertions, 17 deletions
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index 66103e6ff..0927adc07 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -16,7 +16,6 @@ package com.android.incallui; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.base.Preconditions; @@ -26,9 +25,12 @@ import android.os.Message; import android.telecomm.Phone; import android.telephony.DisconnectCause; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; /** * Maintains the list of active calls and notifies interested classes of changes to the call list @@ -48,7 +50,13 @@ public class CallList implements InCallPhoneListener { private final HashMap<String, Call> mCallById = new HashMap<>(); private final HashMap<android.telecomm.Call, Call> mCallByTelecommCall = new HashMap<>(); private final HashMap<String, List<String>> mCallTextReponsesMap = Maps.newHashMap(); - private final Set<Listener> mListeners = Sets.newHashSet(); + /** + * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is + * load factor before resizing, 1 means we only expect a single thread to + * access the map so make only a single shard + */ + private final Set<Listener> mListeners = Collections.newSetFromMap( + new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1)); private final HashMap<String, List<CallUpdateListener>> mCallUpdateListenerMap = Maps .newHashMap(); @@ -151,7 +159,7 @@ public class CallList implements InCallPhoneListener { public void addCallUpdateListener(String callId, CallUpdateListener listener) { List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId); if (listeners == null) { - listeners = Lists.newArrayList(); + listeners = new CopyOnWriteArrayList<CallUpdateListener>(); mCallUpdateListenerMap.put(callId, listeners); } listeners.add(listener); @@ -180,8 +188,9 @@ public class CallList implements InCallPhoneListener { } public void removeListener(Listener listener) { - Preconditions.checkNotNull(listener); - mListeners.remove(listener); + if (listener != null) { + mListeners.remove(listener); + } } /** diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index 23775212a..38b5f5400 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -57,8 +57,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { */ private final Set<InCallStateListener> mListeners = Collections.newSetFromMap( new ConcurrentHashMap<InCallStateListener, Boolean>(8, 0.9f, 1)); - private final List<IncomingCallListener> mIncomingCallListeners = - new CopyOnWriteArrayList<IncomingCallListener>(); + private final List<IncomingCallListener> mIncomingCallListeners = new CopyOnWriteArrayList<>(); private final Set<InCallDetailsListener> mDetailsListeners = Collections.newSetFromMap( new ConcurrentHashMap<InCallDetailsListener, Boolean>(8, 0.9f, 1)); private final Set<InCallOrientationListener> mOrientationListeners = Collections.newSetFromMap( diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java b/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java index 0e64cd790..9f3f062cd 100644 --- a/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java +++ b/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java @@ -17,9 +17,10 @@ package com.android.incallui; import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; +import java.util.Collections; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Class used by {@link InCallService.VideoCallListener} to notify interested parties of incoming @@ -31,10 +32,18 @@ public class InCallVideoCallListenerNotifier { */ private static InCallVideoCallListenerNotifier sInstance = new InCallVideoCallListenerNotifier(); + /** + * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is + * load factor before resizing, 1 means we only expect a single thread to + * access the map so make only a single shard + */ private final Set<SessionModificationListener> mSessionModificationListeners = - Sets.newHashSet(); - private final Set<VideoEventListener> mVideoEventListeners = Sets.newHashSet(); - private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Sets.newHashSet(); + Collections.newSetFromMap(new ConcurrentHashMap<SessionModificationListener, Boolean> + (8, 0.9f, 1)); + private final Set<VideoEventListener> mVideoEventListeners = Collections.newSetFromMap( + new ConcurrentHashMap<VideoEventListener, Boolean>(8, 0.9f, 1)); + private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap( + new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1)); /** * Static singleton accessor method. @@ -65,8 +74,9 @@ public class InCallVideoCallListenerNotifier { * @param listener The listener. */ public void removeSessionModificationListener(SessionModificationListener listener) { - Preconditions.checkNotNull(listener); - mSessionModificationListeners.remove(listener); + if (listener != null) { + mSessionModificationListeners.remove(listener); + } } /** @@ -85,8 +95,9 @@ public class InCallVideoCallListenerNotifier { * @param listener The listener. */ public void removeVideoEventListener(VideoEventListener listener) { - Preconditions.checkNotNull(listener); - mVideoEventListeners.remove(listener); + if (listener != null) { + mVideoEventListeners.remove(listener); + } } /** @@ -105,8 +116,9 @@ public class InCallVideoCallListenerNotifier { * @param listener The listener. */ public void removeSurfaceChangeListener(SurfaceChangeListener listener) { - Preconditions.checkNotNull(listener); - mSurfaceChangeListeners.remove(listener); + if (listener != null) { + mSurfaceChangeListeners.remove(listener); + } } /** |