Merge "Change log behavior to log conference calls only when they are disconnected from conference."
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index cc80db2..514e5f3 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1451,6 +1451,10 @@
         mConnectTimeMillis = connectTimeMillis;
     }
 
+    public void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
+        mConnectElapsedTimeMillis = connectElapsedTimeMillis;
+    }
+
     int getConnectionCapabilities() {
         return mConnectionCapabilities;
     }
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 778c824..d355a65 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -161,22 +161,11 @@
                 newState == CallState.DISCONNECTED || newState == CallState.ABORTED;
         boolean isCallCanceled = isNewlyDisconnected && disconnectCause == DisconnectCause.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
-        // 4) Call is not an external call
-        // 5) Call is not a self-managed call OR call is a self-managed call which has indicated it
-        //    should be logged in its PhoneAccount
-        if (isNewlyDisconnected &&
-                (oldState != CallState.SELECT_PHONE_ACCOUNT &&
-                        !call.isConference() &&
-                        !isCallCanceled) &&
-                !call.isExternalCall() &&
-                (!call.isSelfManaged() ||
-                        (call.isLoggedSelfManaged() &&
-                                (call.getHandoverState() == HandoverState.HANDOVER_NONE ||
-                                call.getHandoverState() == HandoverState.HANDOVER_COMPLETE)))) {
+        if (!isNewlyDisconnected) {
+            return;
+        }
+
+        if (shouldLogDisconnectedCall(call, oldState, isCallCanceled)) {
             int type;
             if (!call.isIncoming()) {
                 type = Calls.OUTGOING_TYPE;
@@ -196,6 +185,60 @@
         }
     }
 
+    /**
+     * Log newly disconnected calls only if all of below conditions are met:
+     * 1) Call was NOT in the "choose account" phase when disconnected
+     * 2) Call is NOT a conference call
+     * 3) Call is NOT simulating a single party conference.
+     * 4) Call was NOT explicitly canceled, except for disconnecting from a conference.
+     * 5) Call is NOT an external call
+     * 6) Call is NOT disconnected because of merging into a conference.
+     * 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it
+     *    should be logged in its PhoneAccount
+     */
+    private boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) {
+        // 1) "Choose account" phase when disconnected
+        if (oldState == CallState.SELECT_PHONE_ACCOUNT) {
+            return false;
+        }
+        // 2) A conference call
+        if (call.isConference()) {
+            return false;
+        }
+
+        DisconnectCause cause = call.getDisconnectCause();
+        if (isCallCanceled) {
+            // 3) No log when disconnecting to simulate a single party conference.
+            if (cause != null
+                    && DisconnectCause.REASON_EMULATING_SINGLE_CALL.equals(cause.getReason())) {
+                return false;
+            }
+            // 4) Explicitly canceled
+            // Conference children connections only have CAPABILITY_DISCONNECT_FROM_CONFERENCE.
+            // Log them when they are disconnected from conference.
+            return Connection.can(call.getConnectionCapabilities(),
+                    Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
+        }
+        // 5) An external call
+        if (call.isExternalCall()) {
+            return false;
+        }
+
+        // 6) Call merged into conferences.
+        if (cause != null && android.telephony.DisconnectCause.toString(
+                android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY)
+                .equals(cause.getReason())) {
+            return false;
+        }
+
+        boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged()
+                && (call.getHandoverState() == HandoverState.HANDOVER_NONE
+                || call.getHandoverState() == HandoverState.HANDOVER_COMPLETE);
+        // 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it
+        //    should be logged in its PhoneAccount
+        return !call.isSelfManaged() || shouldCallSelfManagedLogged;
+    }
+
     void logCall(Call call, int type, boolean showNotificationForMissedCall, CallFilteringResult
             result) {
         if ((type == Calls.MISSED_TYPE || type == Calls.BLOCKED_TYPE) &&
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ff25484..2e5e7af 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -4454,6 +4454,7 @@
 
     public void resetConnectionTime(Call call) {
         call.setConnectTimeMillis(System.currentTimeMillis());
+        call.setConnectElapsedTimeMillis(SystemClock.elapsedRealtime());
         if (mCalls.contains(call)) {
             for (CallsManagerListener listener : mListeners) {
                 listener.onConnectionTimeChanged(call);