Handle unknown connection from technology swap

If an unknown connection is seen from the Telephony layer, attempt to
use it to replace a matching, existing connection, since that
situation may occur when the underlying technology changes in the
radio layer.

Bug: 19363089
Change-Id: Ibe5b89cc791c2d7e8dfac91c47636361796d68cd
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 75f472b..cbcf8bb 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -25,8 +25,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.UserHandle;
-import android.telecom.CallState;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
@@ -212,15 +210,21 @@
     }
 
     private void addNewUnknownCall(Connection connection) {
-        Bundle extras = null;
-        if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
-                !TextUtils.isEmpty(connection.getAddress())) {
-            extras = new Bundle();
-            Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
-            extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, uri);
+        Log.i(this, "addNewUnknownCall, connection is: %s", connection);
+        if (!maybeSwapAnyWithUnknownConnection(connection)) {
+            Log.i(this, "determined new connection is: %s", connection);
+            Bundle extras = null;
+            if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
+                    !TextUtils.isEmpty(connection.getAddress())) {
+                extras = new Bundle();
+                Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
+                extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, uri);
+            }
+            TelecomManager.from(mPhoneProxy.getContext()).addNewUnknownCall(
+                    PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy), extras);
+        } else {
+            Log.i(this, "swapped an old connection, new one is: %s", connection);
         }
-        TelecomManager.from(mPhoneProxy.getContext()).addNewUnknownCall(
-                PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy), extras);
     }
 
     /**
@@ -237,4 +241,45 @@
         TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
                 PhoneUtils.makePstnPhoneAccountHandle(mPhoneProxy), extras);
     }
+
+    /**
+     * Define cait.Connection := com.android.internal.telephony.Connection
+     *
+     * Given a previously unknown cait.Connection, check to see if it's likely a replacement for
+     * another cait.Connnection we already know about. If it is, then we silently swap it out
+     * underneath within the relevant {@link TelephonyConnection}, using
+     * {@link TelephonyConnection#setOriginalConnection(Connection)}, and return {@code true}.
+     * Otherwise, we return {@code false}.
+     */
+    private boolean maybeSwapAnyWithUnknownConnection(Connection unknown) {
+        if (!unknown.isIncoming()) {
+            TelecomAccountRegistry registry = TelecomAccountRegistry.getInstance(null);
+            if (registry != null) {
+                TelephonyConnectionService service = registry.getTelephonyConnectionService();
+                if (service != null) {
+                    for (android.telecom.Connection telephonyConnection : service
+                            .getAllConnections()) {
+                        if (maybeSwapWithUnknownConnection(
+                                (TelephonyConnection) telephonyConnection,
+                                unknown)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean maybeSwapWithUnknownConnection(
+            TelephonyConnection telephonyConnection,
+            Connection unknown) {
+        Connection original = telephonyConnection.getOriginalConnection();
+        if (original != null && !original.isIncoming()
+                && Objects.equals(original.getAddress(), unknown.getAddress())) {
+            telephonyConnection.setOriginalConnection(unknown);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 6a533ef..1cb6442 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -204,6 +204,10 @@
     private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
     private int mServiceState = ServiceState.STATE_POWER_OFF;
 
+    // TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
+    // pattern; redesign. This was added to fix a late release bug.
+    private TelephonyConnectionService mTelephonyConnectionService;
+
     TelecomAccountRegistry(Context context) {
         mContext = context;
         mTelecomManager = TelecomManager.from(context);
@@ -212,12 +216,20 @@
     }
 
     static synchronized final TelecomAccountRegistry getInstance(Context context) {
-        if (sInstance == null) {
+        if (sInstance == null && context != null) {
             sInstance = new TelecomAccountRegistry(context);
         }
         return sInstance;
     }
 
+    void setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService) {
+        this.mTelephonyConnectionService = telephonyConnectionService;
+    }
+
+    TelephonyConnectionService getTelephonyConnectionService() {
+        return mTelephonyConnectionService;
+    }
+
     /**
      * Sets up all the phone accounts for SIMs on first boot.
      */
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 1a2ef53..57f40e9 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -22,7 +22,6 @@
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.ConnectionService;
-import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneNumberUtils;
@@ -74,6 +73,7 @@
         super.onCreate();
         mExpectedComponentName = new ComponentName(this, this.getClass());
         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
+        TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
     }
 
     @Override