Handle Concurrency issues in Connection

Currently, there is a possibility of concurrent thread operations to the
Extras bundle in Conference/Connection. This can cause unexpected
behavior. We have added a lock on the Extras to prevent that from
occuring.

Bug: 29330310
Change-Id: Id63a9797c2f748120a3df8e3ce06c4ce3891c651
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ef314f3..ff220f3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1239,6 +1239,7 @@
     private Conference mConference;
     private ConnectionService mConnectionService;
     private Bundle mExtras;
+    private final Object mExtrasLock = new Object();
 
     /**
      * Tracks the key set for the extras bundle provided on the last invocation of
@@ -1388,7 +1389,13 @@
      * @return The extras associated with this connection.
      */
     public final Bundle getExtras() {
-        return mExtras;
+        Bundle extras = null;
+        synchronized (mExtrasLock) {
+            if (mExtras != null) {
+                extras = new Bundle(mExtras);
+            }
+        }
+        return extras;
     }
 
     /**
@@ -1924,14 +1931,20 @@
         if (extras == null) {
             return;
         }
-
-        if (mExtras == null) {
-            mExtras = new Bundle();
+        // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
+        // the listeners.
+        Bundle listenerExtras;
+        synchronized (mExtrasLock) {
+            if (mExtras == null) {
+                mExtras = new Bundle();
+            }
+            mExtras.putAll(extras);
+            listenerExtras = new Bundle(mExtras);
         }
-        mExtras.putAll(extras);
-
         for (Listener l : mListeners) {
-            l.onExtrasChanged(this, extras);
+            // Create a new clone of the extras for each listener so that they don't clobber
+            // each other
+            l.onExtrasChanged(this, new Bundle(listenerExtras));
         }
     }
 
@@ -1981,18 +1994,16 @@
      * @hide
      */
     public final void removeExtras(List<String> keys) {
-        if (mExtras != null) {
-            for (String key : keys) {
-                mExtras.remove(key);
-            }
-
-            if (mExtras.size() == 0) {
-                mExtras = null;
+        synchronized (mExtrasLock) {
+            if (mExtras != null) {
+                for (String key : keys) {
+                    mExtras.remove(key);
+                }
             }
         }
-
+        List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
         for (Listener l : mListeners) {
-            l.onExtrasRemoved(this, keys);
+            l.onExtrasRemoved(this, unmodifiableKeys);
         }
     }
 
@@ -2274,8 +2285,14 @@
      * @hide
      */
     final void handleExtrasChanged(Bundle extras) {
-        mExtras = extras;
-        onExtrasChanged(mExtras);
+        Bundle b = null;
+        synchronized (mExtrasLock) {
+            mExtras = extras;
+            if (mExtras != null) {
+                b = new Bundle(mExtras);
+            }
+        }
+        onExtrasChanged(b);
     }
 
     /**