release-request-fca2b5ac-03eb-4055-a549-b4fc2b292b64-for-git_oc-release-4049993 snap-temp-L04900000068539456

Change-Id: Ibf15bc238feea7fc70d46ead8f8ddb8722f21bf7
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index ecdf105..7ca9f11 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1870,6 +1870,23 @@
         }
     }
 
+    /**
+     * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
+     * have this call as a child.
+     * @param parentCall
+     */
+    void setParentAndChildCall(Call parentCall) {
+        setParentCall(parentCall);
+        setChildOf(parentCall);
+    }
+
+    /**
+     * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
+     * the child.
+     * TODO: This is only required when adding existing connections as a workaround so that we
+     * can avoid sending the "onParentChanged" callback until later.
+     * @param parentCall The new parent call.
+     */
     void setParentCall(Call parentCall) {
         if (parentCall == this) {
             Log.e(this, new Exception(), "setting the parent to self");
@@ -1879,20 +1896,35 @@
             // nothing to do
             return;
         }
-        Preconditions.checkState(parentCall == null || mParentCall == null);
-
-        Call oldParent = mParentCall;
         if (mParentCall != null) {
             mParentCall.removeChildCall(this);
         }
         mParentCall = parentCall;
-        if (mParentCall != null) {
-            mParentCall.addChildCall(this);
+    }
+
+    /**
+     * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
+     * this call as a child of another call.
+     * <p>
+     * Note: The fact that the {@link Listener#onParentChanged(Call)} callback is called here seems
+     * counter-intuitive; it is done here so that when this method is called from
+     * {@link CallsManager#createCallForExistingConnection(String, ParcelableConnection)} we can
+     * delay informing InCallServices of the change in parent relationship until AFTER the call has
+     * been added to Telecom.
+     * @param parentCall The new parent for this call.
+     */
+    void setChildOf(Call parentCall) {
+        if (parentCall == null) {
+            return;
         }
 
-        Log.addEvent(this, LogUtils.Events.SET_PARENT, mParentCall);
-        for (Listener l : mListeners) {
-            l.onParentChanged(this);
+        if (!parentCall.getChildCalls().contains(this)) {
+            parentCall.addChildCall(this);
+
+            Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
+            for (Listener l : mListeners) {
+                l.onParentChanged(this);
+            }
         }
     }
 
@@ -2011,7 +2043,7 @@
      * that the insurance policy lives in the framework side of things.
      */
     private void fixParentAfterDisconnect() {
-        setParentCall(null);
+        setParentAndChildCall(null);
     }
 
     /**
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index eb44273..499061d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -2069,7 +2069,7 @@
         Trace.beginSection("removeCall");
         Log.v(this, "removeCall(%s)", call);
 
-        call.setParentCall(null);  // need to clean up parent relationship before destroying.
+        call.setParentAndChildCall(null);  // clean up parent relationship before destroying.
         call.removeListener(this);
         call.clearConnectionService();
         // TODO: clean up RTT pipes
@@ -2520,7 +2520,29 @@
         if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
             call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
         }
+        Log.i(this, "createCallForExistingConnection: %s", connection);
+        Call parentCall = null;
+        if (!TextUtils.isEmpty(connection.getParentCallId())) {
+            String parentId = connection.getParentCallId();
+            parentCall = mCalls
+                    .stream()
+                    .filter(c -> c.getId().equals(parentId))
+                    .findFirst()
+                    .orElse(null);
+            if (parentCall != null) {
+                Log.i(this, "createCallForExistingConnection: %s added as child of %s.",
+                        call.getId(),
+                        parentCall.getId());
+                // Set JUST the parent property, which won't send an update to the Incall UI.
+                call.setParentCall(parentCall);
+            }
+        }
         addCall(call);
+        if (parentCall != null) {
+            // Now, set the call as a child of the parent since it has been added to Telecom.  This
+            // is where we will inform InCall.
+            call.setChildOf(parentCall);
+        }
 
         return call;
     }
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 9104efe..b395adc 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -329,10 +329,10 @@
                     if (childCall != null) {
                         if (conferenceCallId == null) {
                             Log.d(this, "unsetting parent: %s", conferenceCallId);
-                            childCall.setParentCall(null);
+                            childCall.setParentAndChildCall(null);
                         } else {
                             Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
-                            childCall.setParentCall(conferenceCall);
+                            childCall.setParentAndChildCall(conferenceCall);
                         }
                     } else {
                         // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
@@ -445,7 +445,7 @@
                         Call childCall = mCallIdMapper.getCall(connId);
                         Log.d(this, "found child: %s", connId);
                         if (childCall != null) {
-                            childCall.setParentCall(conferenceCall);
+                            childCall.setParentAndChildCall(conferenceCall);
                         }
                     }
                 }