Remote incoming calls: Telephony

Change-Id: Ie8091aacfd9be36d63db02eb8a0a4935753d657f
diff --git a/sip/src/com/android/services/telephony/sip/SipConnection.java b/sip/src/com/android/services/telephony/sip/SipConnection.java
index 5b065fd..5bca97b 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnection.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnection.java
@@ -40,7 +40,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_PRECISE_CALL_STATE_CHANGED:
-                    updateState();
+                    updateState(false);
                     break;
             }
         }
@@ -210,14 +210,14 @@
                 call.getEarliestConnection() == mOriginalConnection;
     }
 
-    private void updateState() {
+    private void updateState(boolean force) {
         if (mOriginalConnection == null) {
             return;
         }
 
         Call.State newState = mOriginalConnection.getState();
         if (VERBOSE) log("updateState, " + mOriginalConnectionState + " -> " + newState);
-        if (mOriginalConnectionState != newState) {
+        if (force || mOriginalConnectionState != newState) {
             mOriginalConnectionState = newState;
             switch (newState) {
                 case IDLE:
@@ -243,7 +243,7 @@
                 case DISCONNECTING:
                     break;
             }
-            updateCallCapabilities();
+            updateCallCapabilities(force);
         }
     }
 
@@ -255,16 +255,17 @@
         return capabilities;
     }
 
-    void updateCallCapabilities() {
+    void updateCallCapabilities(boolean force) {
         int newCallCapabilities = buildCallCapabilities();
-        if (getCallCapabilities() != newCallCapabilities) {
+        if (force || getCallCapabilities() != newCallCapabilities) {
             setCallCapabilities(newCallCapabilities);
         }
     }
 
     void onAddedToCallService() {
         if (VERBOSE) log("onAddedToCallService");
-        updateCallCapabilities();
+        updateState(true);
+        updateCallCapabilities(true);
         setAudioModeIsVoip(true);
         if (mOriginalConnection != null) {
             setCallerDisplayName(mOriginalConnection.getCnapName(),
diff --git a/sip/src/com/android/services/telephony/sip/SipConnectionService.java b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
index 3c32604..a46180d 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnectionService.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
@@ -54,15 +54,15 @@
     }
 
     @Override
-    protected void onCreateConnections(
+    protected void onCreateOutgoingConnection(
             final ConnectionRequest request,
-            final OutgoingCallResponse<Connection> response) {
-        if (VERBOSE) log("onCreateConnections, request: " + request);
+            final CreateConnectionResponse<Connection> response) {
+        if (VERBOSE) log("onCreateOutgoingConnection, request: " + request);
 
         SipProfileChooser.Callback callback = new SipProfileChooser.Callback() {
             @Override
             public void onSipChosen(SipProfile profile) {
-                if (VERBOSE) log("onCreateConnections, onSipChosen: " + profile);
+                if (VERBOSE) log("onCreateOutgoingConnection, onSipChosen: " + profile);
                 SipConnection connection = createConnectionForProfile(profile, request);
                 if (connection == null) {
                     response.onCancel(request);
@@ -73,13 +73,13 @@
 
             @Override
             public void onSipNotChosen() {
-                if (VERBOSE) log("onCreateConnections, onSipNotChosen");
+                if (VERBOSE) log("onCreateOutgoingConnection, onSipNotChosen");
                 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null);
             }
 
             @Override
             public void onCancelCall() {
-                if (VERBOSE) log("onCreateConnections, onCancelCall");
+                if (VERBOSE) log("onCreateOutgoingConnection, onCancelCall");
                 response.onCancel(request);
             }
         };
@@ -99,14 +99,20 @@
     @Override
     protected void onCreateIncomingConnection(
             ConnectionRequest request,
-            Response<ConnectionRequest, Connection> response) {
+            CreateConnectionResponse<Connection> response) {
         if (VERBOSE) log("onCreateIncomingConnection, request: " + request);
 
+        if (request.getExtras() == null) {
+            if (VERBOSE) log("onCreateIncomingConnection, no extras");
+            response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+            return;
+        }
+
         Intent sipIntent = (Intent) request.getExtras().getParcelable(
                 SipUtil.EXTRA_INCOMING_CALL_INTENT);
         if (sipIntent == null) {
             if (VERBOSE) log("onCreateIncomingConnection, no SIP intent");
-            response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+            response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null);
             return;
         }
 
@@ -114,8 +120,8 @@
         try {
             sipAudioCall = SipManager.newInstance(this).takeAudioCall(sipIntent, null);
         } catch (SipException e) {
-            log("onCreateConferenceConnection, takeAudioCall exception: " + e);
-            response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+            log("onCreateIncomingConnection, takeAudioCall exception: " + e);
+            response.onCancel(request);
             return;
         }
 
@@ -129,11 +135,11 @@
             if (VERBOSE) log("onCreateIncomingConnection, new connection: " + originalConnection);
             if (originalConnection != null) {
                 SipConnection connection = new SipConnection(originalConnection);
-                response.onResult(getConnectionRequestForIncomingCall(request, originalConnection),
+                response.onSuccess(getConnectionRequestForIncomingCall(request, originalConnection),
                         connection);
             } else {
                 if (VERBOSE) log("onCreateIncomingConnection, takingIncomingCall failed");
-                response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, null);
+                response.onCancel(request);
             }
         }
     }
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 95d648c..c3d0191 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -2251,7 +2251,7 @@
         CharSequence label = mConnectionServiceLabelByComponentName.get(
                 value == null ? mConnectionService.getValue() : value);
         if (label == null) {
-            Log.wtf(LOG_TAG, "Unknown default connection service entry " +
+            Log.w(LOG_TAG, "Unknown default connection service entry " +
                     mConnectionService.getValue());
             mConnectionService.setSummary("");
         }
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index f34cf27..8b02b52 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -51,7 +51,7 @@
     void setIsConferenceCapable(boolean isConferenceCapable) {
         if (mIsConferenceCapable != isConferenceCapable) {
             mIsConferenceCapable = isConferenceCapable;
-            updateCallCapabilities();
+            updateCallCapabilities(false);
         }
     }
 
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 4662536..5886ef2 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,6 +16,7 @@
 
 package com.android.services.telephony;
 
+import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
@@ -29,6 +30,7 @@
 import android.telecomm.Connection;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Base class for CDMA and GSM connections.
@@ -40,21 +42,20 @@
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            // TODO: This code assumes that there is only one connection in the foreground call,
-            // in other words, it punts on network-mediated conference calling.
-            if (getOriginalConnection() != getForegroundConnection()) {
-                Log.v(TelephonyConnection.this, "handleMessage, original connection is not " +
-                        "foreground connection, skipping");
-                return;
-            }
-
             switch (msg.what) {
                 case MSG_PRECISE_CALL_STATE_CHANGED:
                     Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED");
-                    updateState();
+                    updateState(false);
                     break;
                 case MSG_RINGBACK_TONE:
                     Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE");
+                    // TODO: This code assumes that there is only one connection in the foreground
+                    // call, in other words, it punts on network-mediated conference calling.
+                    if (getOriginalConnection() != getForegroundConnection()) {
+                        Log.v(TelephonyConnection.this, "handleMessage, original connection is " +
+                                "not foreground connection, skipping");
+                        return;
+                    }
                     setRequestingRingback((Boolean) ((AsyncResult) msg.obj).result);
                     break;
             }
@@ -81,7 +82,6 @@
                 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
         mOriginalConnection.addPostDialListener(mPostDialListener);
-        updateState();
     }
 
     @Override
@@ -223,23 +223,40 @@
 
     protected abstract int buildCallCapabilities();
 
-    final void updateCallCapabilities() {
+    protected final void updateCallCapabilities(boolean force) {
         int newCallCapabilities = buildCallCapabilities();
-        if (getCallCapabilities() != newCallCapabilities) {
+        if (force || getCallCapabilities() != newCallCapabilities) {
             setCallCapabilities(newCallCapabilities);
         }
     }
 
-    void onAddedToCallService() {
-        updateCallCapabilities();
+    protected final void updateHandle(boolean force) {
         if (mOriginalConnection != null) {
-            setCallerDisplayName(
-                    mOriginalConnection.getCnapName(),
-                    mOriginalConnection.getCnapNamePresentation());
+            Uri handle = TelephonyConnectionService.getHandleFromAddress(
+                    mOriginalConnection.getAddress());
+            int presentation = mOriginalConnection.getNumberPresentation();
+            if (force || !Objects.equals(handle, getHandle()) ||
+                    presentation != getHandlePresentation()) {
+                Log.v(this, "updateHandle, handle changed");
+                setHandle(handle, presentation);
+            }
+
+            String name = mOriginalConnection.getCnapName();
+            int namePresentation = mOriginalConnection.getCnapNamePresentation();
+            if (force || !Objects.equals(name, getCallerDisplayName()) ||
+                    namePresentation != getCallerDisplayNamePresentation()) {
+                Log.v(this, "updateHandle, caller display name changed");
+                setCallerDisplayName(name, namePresentation);
+            }
         }
     }
 
+    void onAddedToCallService() {
+        updateState(false);
+    }
+
     void onRemovedFromCallService() {
+        // Subclass can override this to do cleanup.
     }
 
     private void hangup(int disconnectCause) {
@@ -313,14 +330,14 @@
         return true;
     }
 
-    private void updateState() {
+    private void updateState(boolean force) {
         if (mOriginalConnection == null) {
             return;
         }
 
         Call.State newState = mOriginalConnection.getState();
         Log.v(this, "Update state from %s to %s for %s", mOriginalConnectionState, newState, this);
-        if (mOriginalConnectionState != newState) {
+        if (force || mOriginalConnectionState != newState) {
             mOriginalConnectionState = newState;
             switch (newState) {
                 case IDLE:
@@ -341,12 +358,14 @@
                     break;
                 case DISCONNECTED:
                     setDisconnected(mOriginalConnection.getDisconnectCause(), null);
+                    close();
                     break;
                 case DISCONNECTING:
                     break;
             }
-            updateCallCapabilities();
         }
+        updateCallCapabilities(force);
+        updateHandle(force);
     }
 
     private void close() {
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 782f864..626244e 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -53,20 +53,21 @@
     }
 
     @Override
-    protected void onCreateConnections(
+    protected void onCreateOutgoingConnection(
             final ConnectionRequest request,
-            final OutgoingCallResponse<Connection> response) {
-        Log.v(this, "onCreateConnections, request: " + request);
+            final CreateConnectionResponse<Connection> response) {
+        Log.v(this, "onCreateOutgoingConnection, request: " + request);
 
         Uri handle = request.getHandle();
         if (handle == null) {
-            Log.d(this, "onCreateConnections, handle is null");
+            Log.d(this, "onCreateOutgoingConnection, handle is null");
             response.onFailure(request, DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, "Handle is null");
             return;
         }
 
         if (!SCHEME_TEL.equals(handle.getScheme())) {
-            Log.d(this, "onCreateConnections, Handle %s is not type tel", handle.getScheme());
+            Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel",
+                    handle.getScheme());
             response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED,
                     "Handle scheme is not type tel");
             return;
@@ -74,14 +75,14 @@
 
         final String number = handle.getSchemeSpecificPart();
         if (TextUtils.isEmpty(number)) {
-            Log.d(this, "onCreateConnections, unable to parse number");
+            Log.d(this, "onCreateOutgoingConnection, unable to parse number");
             response.onFailure(request, DisconnectCause.INVALID_NUMBER, "Unable to parse number");
             return;
         }
 
         final Phone phone = PhoneFactory.getDefaultPhone();
         if (phone == null) {
-            Log.d(this, "onCreateConnections, phone is null");
+            Log.d(this, "onCreateOutgoingConnection, phone is null");
             response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, "Phone is null");
             return;
         }
@@ -105,15 +106,16 @@
                             "ServiceState.STATE_POWER_OFF");
                     return;
                 default:
-                    Log.d(this, "onCreateConnections, Unrecognized service state: %d", state);
+                    Log.d(this, "onCreateOutgoingConnection, unkown service state: %d", state);
                     response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED,
-                            "Unrecognized service state " + state);
+                            "Unkown service state " + state);
                     return;
             }
         }
 
         if (isEmergencyNumber) {
-            Log.d(this, "onCreateConnections, doing startTurnOnRadioSequence for emergency number");
+            Log.d(this, "onCreateOutgoingConnection, doing startTurnOnRadioSequence for " +
+                    "emergency number");
             if (mEmergencyCallHelper == null) {
                 mEmergencyCallHelper = new EmergencyCallHelper(this);
             }
@@ -124,7 +126,7 @@
                             if (isRadioReady) {
                                 startOutgoingCall(request, response, phone, number);
                             } else {
-                                Log.d(this, "onCreateConnections, failed to turn on radio");
+                                Log.d(this, "onCreateOutgoingConnection, failed to turn on radio");
                                 response.onFailure(request, DisconnectCause.POWER_OFF,
                                         "Failed to turn on radio.");
                             }
@@ -153,22 +155,21 @@
     @Override
     protected void onCreateIncomingConnection(
             ConnectionRequest request,
-            Response<ConnectionRequest, Connection> response) {
+            CreateConnectionResponse<Connection> response) {
         Log.v(this, "onCreateIncomingConnection, request: " + request);
 
         Phone phone = PhoneFactory.getDefaultPhone();
         Call call = phone.getRingingCall();
         if (!call.getState().isRinging()) {
             Log.v(this, "onCreateIncomingConnection, no ringing call");
-            response.onError(request, DisconnectCause.INCOMING_MISSED, "Found no ringing call");
+            response.onFailure(request, DisconnectCause.INCOMING_MISSED, "Found no ringing call");
             return;
         }
 
         com.android.internal.telephony.Connection originalConnection = call.getEarliestConnection();
         if (isOriginalConnectionKnown(originalConnection)) {
             Log.v(this, "onCreateIncomingConnection, original connection already registered");
-            response.onError(request, DisconnectCause.ERROR_UNSPECIFIED,
-                    "Connection already registered");
+            response.onCancel(request);
             return;
         }
 
@@ -182,11 +183,11 @@
                 request.getVideoState());
 
         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
-            response.onResult(telephonyRequest, new GsmConnection(originalConnection));
+            response.onSuccess(telephonyRequest, new GsmConnection(originalConnection));
         } else if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
-            response.onResult(telephonyRequest, new CdmaConnection(originalConnection));
+            response.onSuccess(telephonyRequest, new CdmaConnection(originalConnection));
         } else {
-            response.onError(request, DisconnectCause.ERROR_UNSPECIFIED, "Invalid phone type");
+            response.onCancel(request);
         }
     }
 
@@ -208,7 +209,7 @@
 
     private void startOutgoingCall(
             ConnectionRequest request,
-            OutgoingCallResponse<Connection> response,
+            CreateConnectionResponse<Connection> response,
             Phone phone,
             String number) {
         Log.v(this, "startOutgoingCall");
@@ -258,7 +259,7 @@
         return false;
     }
 
-    private static Uri getHandleFromAddress(String address) {
+    static Uri getHandleFromAddress(String address) {
         // Address can be null for blocked calls.
         if (address == null) {
             address = "";