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: I55e081eda6ba19fa466bbf019c648bbdaf833c33
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
index 79e777a..03a8676 100644
--- a/telecomm/java/android/telecomm/Phone.java
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -24,6 +24,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A unified virtual device providing a means of voice (and other) communication on a device.
@@ -89,7 +90,7 @@
 
     private AudioState mAudioState;
 
-    private final List<Listener> mListeners = new ArrayList<>();
+    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
 
     /** {@hide} */
     Phone(InCallAdapter adapter) {
@@ -171,7 +172,9 @@
      * @param listener A {@code Listener} object.
      */
     public final void removeListener(Listener listener) {
-        mListeners.remove(listener);
+        if (listener != null) {
+            mListeners.remove(listener);
+        }
     }
 
     /**
@@ -236,30 +239,26 @@
     }
 
     private void fireCallAdded(Call call) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCallAdded(this, call);
+        for (Listener listener : mListeners) {
+            listener.onCallAdded(this, call);
         }
     }
 
     private void fireCallRemoved(Call call) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCallRemoved(this, call);
+        for (Listener listener : mListeners) {
+            listener.onCallRemoved(this, call);
         }
     }
 
     private void fireAudioStateChanged(AudioState audioState) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onAudioStateChanged(this, audioState);
+        for (Listener listener : mListeners) {
+            listener.onAudioStateChanged(this, audioState);
         }
     }
 
     private void fireBringToForeground(boolean showDialpad) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onBringToForeground(this, showDialpad);
+        for (Listener listener : mListeners) {
+            listener.onBringToForeground(this, showDialpad);
         }
     }