Add disconnect cause to the disconnect path.

Change-Id: I4488f58eaf656b46410b2ed85999d0598bc11a5d
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 0789135..88ec312 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -20,6 +20,7 @@
 import android.telecomm.CallInfo;
 import android.telecomm.CallState;
 import android.telecomm.GatewayInfo;
+import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 
 import com.google.android.collect.Sets;
@@ -84,6 +85,17 @@
     private boolean mIsEmergencyCall;
 
     /**
+     * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
+     * See {@link android.telephony.DisconnectCause}.
+     */
+    private int mDisconnectCause;
+
+    /**
+     * Additional disconnect information provided by the call service.
+     */
+    private String mDisconnectMessage;
+
+    /**
      * Creates an empty call object with a unique call ID.
      *
      * @param isIncoming True if this is an incoming call.
@@ -108,6 +120,7 @@
         mGatewayInfo = gatewayInfo;
         mIsIncoming = isIncoming;
         mCreationTime = new Date();
+        mDisconnectCause = DisconnectCause.NOT_VALID;
     }
 
     /** {@inheritDoc} */
@@ -132,6 +145,8 @@
      * and instead keep the code resilient to unexpected state changes.
      */
     void setState(CallState newState) {
+        Preconditions.checkState(newState != CallState.DISCONNECTED ||
+                mDisconnectCause != DisconnectCause.NOT_VALID);
         if (mState != newState) {
             Log.v(this, "setState %s -> %s", mState, newState);
             mState = newState;
@@ -148,6 +163,26 @@
                 mHandle.getSchemeSpecificPart(), TelecommApp.getInstance());
     }
 
+    /**
+     * @param disconnectCause The reason for the disconnection, any of
+     *         {@link android.telephony.DisconnectCause}.
+     * @param disconnectMessage Optional call-service-provided message about the disconnect.
+     */
+    void setDisconnectCause(int disconnectCause, String disconnectMessage) {
+        // TODO: Consider combining this method with a setDisconnected() method that is totally
+        // separate from setState.
+        mDisconnectCause = disconnectCause;
+        mDisconnectMessage = disconnectMessage;
+    }
+
+    int getDisconnectCause() {
+        return mDisconnectCause;
+    }
+
+    String getDisconnectMessage() {
+        return mDisconnectMessage;
+    }
+
     boolean isEmergencyCall() {
         return mIsEmergencyCall;
     }
diff --git a/src/com/android/telecomm/CallServiceAdapter.java b/src/com/android/telecomm/CallServiceAdapter.java
index f95953a..7b1fc3e 100644
--- a/src/com/android/telecomm/CallServiceAdapter.java
+++ b/src/com/android/telecomm/CallServiceAdapter.java
@@ -165,11 +165,12 @@
     /** {@inheritDoc} */
     // TODO(gilad): Ensure that any communication from the underlying ICallService
     // implementation is expected (or otherwise suppressed at the adapter level).
-    @Override public void setDisconnected(final String callId) {
+    @Override public void setDisconnected(
+            final String callId, final int disconnectCause, final String disconnectMessage) {
         checkValidCallId(callId);
         mHandler.post(new Runnable() {
             @Override public void run() {
-                mCallsManager.markCallAsDisconnected(callId);
+                mCallsManager.markCallAsDisconnected(callId, disconnectCause, disconnectMessage);
             }
         });
     }
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index f1bb109..6daaba0 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -23,6 +23,7 @@
 import android.telecomm.CallServiceDescriptor;
 import android.telecomm.CallState;
 import android.telecomm.GatewayInfo;
+import android.telephony.DisconnectCause;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -175,7 +176,12 @@
      * @param call The incoming call.
      */
     void handleUnsuccessfulIncomingCall(Call call) {
-        setCallState(call, CallState.DISCONNECTED);
+        // Incoming calls are not added unless they are successful. We set the state and disconnect
+        // cause just as a matter of good bookkeeping. We do not use the specific methods for
+        // setting those values so that we do not trigger CallsManagerListener events.
+        // TODO: Needs more specific disconnect error for this case.
+        call.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
+        call.setState(CallState.DISCONNECTED);
     }
 
     /**
@@ -229,10 +235,11 @@
         if (isAborted) {
             call.abort();
             setCallState(call, CallState.ABORTED);
+            removeCall(call);
         } else {
-            setCallState(call, CallState.DISCONNECTED);
+            // TODO: Replace disconnect cause with more specific disconnect causes.
+            markCallAsDisconnected(call.getId(), DisconnectCause.ERROR_UNSPECIFIED, null);
         }
-        removeCall(call);
     }
 
     /**
@@ -371,10 +378,16 @@
      * live call, then also disconnect from the in-call controller.
      *
      * @param callId The ID of the call.
+     * @param disconnectCause The disconnect reason, see {@link android.telephony.DisconnectCause}.
+     * @param disconnectMessage Optional call-service-provided message about the disconnect.
      */
-    void markCallAsDisconnected(String callId) {
-        setCallState(callId, CallState.DISCONNECTED);
-        removeCall(mCalls.remove(callId));
+    void markCallAsDisconnected(String callId, int disconnectCause, String disconnectMessage) {
+        Call call = mCalls.get(callId);
+        if (call != null) {
+            call.setDisconnectCause(disconnectCause, disconnectMessage);
+            setCallState(callId, CallState.DISCONNECTED);
+            removeCall(call);
+        }
     }
 
     /**
@@ -387,7 +400,7 @@
         Preconditions.checkNotNull(callService);
         for (Call call : ImmutableList.copyOf(mCalls.values())) {
             if (call.getCallService() == callService) {
-                markCallAsDisconnected(call.getId());
+                markCallAsDisconnected(call.getId(), DisconnectCause.ERROR_UNSPECIFIED, null);
             }
         }
     }
@@ -406,6 +419,7 @@
     }
 
     private void removeCall(Call call) {
+        mCalls.remove(call.getId());
         call.clearCallService();
         for (CallsManagerListener listener : mListeners) {
             listener.onCallRemoved(call);
diff --git a/src/com/android/telecomm/InCallController.java b/src/com/android/telecomm/InCallController.java
index 5c3e3e6..5fbf1f8 100644
--- a/src/com/android/telecomm/InCallController.java
+++ b/src/com/android/telecomm/InCallController.java
@@ -129,7 +129,7 @@
             case DISCONNECTED:
                 Log.i(this, "Mark call as DISCONNECTED: %s", call.getId());
                 try {
-                    mInCallService.setDisconnected(call.getId());
+                    mInCallService.setDisconnected(call.getId(), call.getDisconnectCause());
                 } catch (RemoteException e) {
                     Log.e(this, e, "Exception attempting to call setDisconnected.");
                 }
diff --git a/tests/src/com/android/telecomm/testcallservice/TestCallService.java b/tests/src/com/android/telecomm/testcallservice/TestCallService.java
index 044a398..f3fb95a 100644
--- a/tests/src/com/android/telecomm/testcallservice/TestCallService.java
+++ b/tests/src/com/android/telecomm/testcallservice/TestCallService.java
@@ -25,6 +25,7 @@
 import android.telecomm.CallService;
 import android.telecomm.CallServiceAdapter;
 import android.telecomm.CallState;
+import android.telephony.DisconnectCause;
 import android.util.Log;
 
 import com.android.telecomm.tests.R;
@@ -138,7 +139,7 @@
     /** {@inheritDoc} */
     @Override
     public void reject(String callId) {
-        mTelecommAdapter.setDisconnected(callId);
+        mTelecommAdapter.setDisconnected(callId, DisconnectCause.INCOMING_REJECTED, null);
     }
 
     /** {@inheritDoc} */
@@ -147,7 +148,7 @@
         Log.i(TAG, "disconnect(" + callId + ")");
 
         destroyCall(callId);
-        mTelecommAdapter.setDisconnected(callId);
+        mTelecommAdapter.setDisconnected(callId, DisconnectCause.LOCAL, null);
     }
 
     /** {@inheritDoc} */