Merge "Ensure VideoProvider is only set on ParcelableCalls for the default InCallService (i.e. the Incall UI)." into lmp-dev
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 7718df6..4fa39e8 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -350,6 +350,10 @@
             Log.v(this, "setState %s -> %s", mState, newState);
             mState = newState;
             maybeLoadCannedSmsResponses();
+
+            if (mState == CallState.DISCONNECTED) {
+                fixParentAfterDisconnect();
+            }
         }
     }
 
@@ -364,6 +368,10 @@
         return mRequestingRingback;
     }
 
+    boolean isConference() {
+        return mIsConference;
+    }
+
     Uri getHandle() {
         return mHandle;
     }
@@ -825,7 +833,11 @@
     }
 
     void splitFromConference() {
-        // TODO: todo
+        if (mConnectionService == null) {
+            Log.w(this, "splitting from conference call without a connection service");
+        } else {
+            mConnectionService.splitFromConference(this);
+        }
     }
 
     void setParentCall(Call parentCall) {
@@ -941,6 +953,16 @@
     }
 
     /**
+     * We need to make sure that before we move a call to the disconnected state, it no
+     * longer has any parent/child relationships.  We want to do this to ensure that the InCall
+     * Service always has the right data in the right order.  We also want to do it in telecomm so
+     * that the insurance policy lives in the framework side of things.
+     */
+    private void fixParentAfterDisconnect() {
+        setParentCall(null);
+    }
+
+    /**
      * @return True if the call is ringing, else logs the action name.
      */
     private boolean isRinging(String actionName) {
diff --git a/src/com/android/telecomm/CallLogManager.java b/src/com/android/telecomm/CallLogManager.java
index ee65255..ba02aab 100644
--- a/src/com/android/telecomm/CallLogManager.java
+++ b/src/com/android/telecomm/CallLogManager.java
@@ -89,9 +89,19 @@
 
     @Override
     public void onCallStateChanged(Call call, int oldState, int newState) {
-        if ((newState == CallState.DISCONNECTED || newState == CallState.ABORTED)
-                && oldState != CallState.PRE_DIAL_WAIT
-                && call.getDisconnectCause() != DisconnectCause.OUTGOING_CANCELED) {
+        boolean isNewlyDisconnected =
+                newState == CallState.DISCONNECTED || newState == CallState.ABORTED;
+        boolean isCallCanceled = isNewlyDisconnected &&
+                call.getDisconnectCause() == DisconnectCause.OUTGOING_CANCELED;
+
+        // Log newly disconnected calls only if:
+        // 1) It was not in the "choose account" phase when disconnected
+        // 2) It is a conference call
+        // 3) Call was not explicitly canceled
+        if (isNewlyDisconnected &&
+                (oldState != CallState.PRE_DIAL_WAIT &&
+                 !call.isConference() &&
+                 !isCallCanceled)) {
             int type;
             if (!call.isIncoming()) {
                 type = Calls.OUTGOING_TYPE;
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 4e3a0cb..8935766 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -782,6 +782,7 @@
     private void removeCall(Call call) {
         Log.v(this, "removeCall(%s)", call);
 
+        call.setParentCall(null);  // need to clean up parent relationship before destroying.
         call.removeListener(this);
         call.clearConnectionService();
 
diff --git a/src/com/android/telecomm/ConnectionServiceWrapper.java b/src/com/android/telecomm/ConnectionServiceWrapper.java
index 264ee63..7d1f01a 100644
--- a/src/com/android/telecomm/ConnectionServiceWrapper.java
+++ b/src/com/android/telecomm/ConnectionServiceWrapper.java
@@ -171,6 +171,7 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         Call childCall = mCallIdMapper.getCall(args.arg1);
+                        Log.d(this, "SET_IS_CONFERENCE: %s %s", args.arg1, args.arg2);
                         if (childCall != null) {
                             String conferenceCallId = (String) args.arg2;
                             if (conferenceCallId == null) {
@@ -205,7 +206,8 @@
                         mCallIdMapper.addCall(conferenceCall, id);
                         conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
 
-                        Log.d(this, "adding children to conference");
+                        Log.d(this, "adding children to conference %s",
+                                parcelableConference.getConnectionIds());
                         for (String callId : parcelableConference.getConnectionIds()) {
                             Call childCall = mCallIdMapper.getCall(callId);
                             Log.d(this, "found child: %s", callId);
@@ -456,8 +458,7 @@
         @Override
         public void setIsConferenced(String callId, String conferenceCallId) {
             logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
-            if (mCallIdMapper.isValidCallId(callId) &&
-                    mCallIdMapper.isValidConferenceId(conferenceCallId)) {
+            if (mCallIdMapper.isValidCallId(callId)) {
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = callId;
                 args.arg2 = conferenceCallId;
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
index aacb80b..9e3b5ee 100644
--- a/src/com/android/telecomm/CreateConnectionProcessor.java
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -57,6 +57,24 @@
                     + Objects.toString(connectionManagerPhoneAccount) + ","
                     + Objects.toString(targetPhoneAccount) + ")";
         }
+
+        /**
+         * Determines if this instance of {@code CallAttemptRecord} has the same underlying
+         * {@code PhoneAccountHandle}s as another instance.
+         *
+         * @param obj The other instance to compare against.
+         * @return {@code True} if the {@code CallAttemptRecord}s are equal.
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof CallAttemptRecord) {
+                CallAttemptRecord other = (CallAttemptRecord) obj;
+                return Objects.equals(connectionManagerPhoneAccount,
+                        other.connectionManagerPhoneAccount) &&
+                        Objects.equals(targetPhoneAccount, other.targetPhoneAccount);
+            }
+            return false;
+        }
     }
 
     private final Call mCall;
@@ -212,6 +230,7 @@
             mAttemptRecords.clear();
             List<PhoneAccountHandle> allAccountHandles = TelecommApp.getInstance()
                     .getPhoneAccountRegistrar().getOutgoingPhoneAccounts();
+            // First, add the PSTN phone account
             for (int i = 0; i < allAccountHandles.size(); i++) {
                 if (TelephonyUtil.isPstnComponentName(
                         allAccountHandles.get(i).getComponentName())) {
@@ -222,6 +241,19 @@
                                     allAccountHandles.get(i)));
                 }
             }
+
+            // Next, add the connection manager account as a backup.
+            PhoneAccountHandle callManager = TelecommApp.getInstance()
+                    .getPhoneAccountRegistrar().getSimCallManager();
+            CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManager,
+                    TelecommApp.getInstance().getPhoneAccountRegistrar().
+                            getDefaultOutgoingPhoneAccount());
+
+            if (callManager != null && !mAttemptRecords.contains(callAttemptRecord)) {
+                Log.i(this, "Will try Connection Manager account %s for emergency",
+                        callManager);
+                mAttemptRecords.add(callAttemptRecord);
+            }
         }
     }