Merge "Finalize multiendpoint functionality." into nyc-mr1-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index aae9609..1139cc5 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -343,6 +343,7 @@
     private final CallsManager mCallsManager;
     private final TelecomSystem.SyncRoot mLock;
     private final String mId;
+    private String mConnectionId;
     private Analytics.CallInfo mAnalytics;
 
     private boolean mWasConferencePreviouslyMerged = false;
@@ -379,6 +380,8 @@
      */
     private boolean mIsVideoCallingSupported = false;
 
+    private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
+
     /**
      * Persists the specified parameters and initializes the new instance.
      *
@@ -404,6 +407,7 @@
             ConnectionServiceRepository repository,
             ContactsAsyncHelper contactsAsyncHelper,
             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
+            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             Uri handle,
             GatewayInfo gatewayInfo,
             PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -412,11 +416,13 @@
             boolean shouldAttachToExistingConnection,
             boolean isConference) {
         mId = callId;
+        mConnectionId = callId;
         mState = isConference ? CallState.ACTIVE : CallState.NEW;
         mContext = context;
         mCallsManager = callsManager;
         mLock = lock;
         mRepository = repository;
+        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
         setHandle(handle);
         mPostDialDigits = handle != null
                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
@@ -458,6 +464,7 @@
             ConnectionServiceRepository repository,
             ContactsAsyncHelper contactsAsyncHelper,
             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
+            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             Uri handle,
             GatewayInfo gatewayInfo,
             PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -467,7 +474,7 @@
             boolean isConference,
             long connectTimeMillis) {
         this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
-                callerInfoAsyncQueryFactory, handle, gatewayInfo,
+                callerInfoAsyncQueryFactory, phoneNumberUtilsAdapter, handle, gatewayInfo,
                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
                 shouldAttachToExistingConnection, isConference);
 
@@ -595,6 +602,21 @@
     }
 
     /**
+     * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
+     * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
+     * @return The call ID with an appended attempt id.
+     */
+    public String getConnectionId() {
+        if(mCreateConnectionProcessor != null) {
+            mConnectionId = mId + "_" +
+                    String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
+            return mConnectionId;
+        } else {
+            return mConnectionId;
+        }
+    }
+
+    /**
      * Sets the call state. Although there exists the notion of appropriate state transitions
      * (see {@link CallState}), in practice those expectations break down when cellular systems
      * misbehave and they do this very often. The result is that we do not enforce state transitions
@@ -750,8 +772,9 @@
             // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
             // call, it will remain so for the rest of it's lifetime.
             if (!mIsEmergencyCall) {
-                mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
-                        mContext, mHandle.getSchemeSpecificPart());
+                mIsEmergencyCall = mHandle != null &&
+                        mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
+                                mHandle.getSchemeSpecificPart());
             }
             startCallerInfoLookup();
             for (Listener l : mListeners) {
@@ -1674,7 +1697,7 @@
             return false;
         }
 
-        if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
+        if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
             // The incoming number is actually a URI (i.e. a SIP address),
             // not a regular PSTN phone number, and we can't send SMSes to
             // SIP addresses.
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index b097bea..da2c199 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -77,7 +77,16 @@
         }
     }
 
+    public interface ICallInfo {
+        String getCallId(Call call);
+    }
+
     private final BiMap<String, Call> mCalls = new BiMap<>();
+    private ICallInfo mCallInfo;
+
+    public CallIdMapper(ICallInfo callInfo) {
+        mCallInfo = callInfo;
+    }
 
     void replaceCall(Call newCall, Call callToReplace) {
         // Use the old call's ID for the new call.
@@ -93,7 +102,7 @@
     }
 
     void addCall(Call call) {
-        addCall(call, call.getId());
+        addCall(call, mCallInfo.getCallId(call));
     }
 
     void removeCall(Call call) {
@@ -111,7 +120,7 @@
         if (call == null || mCalls.getKey(call) == null) {
             return null;
         }
-        return call.getId();
+        return mCallInfo.getCallId(call);
     }
 
     Call getCall(Object objId) {
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index ffa76ce..1219216 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -148,7 +148,7 @@
             // process will be running throughout the duration of the phone call and should never
             // be killed.
             NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
-                    context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(),
+                    context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                     isPrivilegedDialer);
             final int result = broadcaster.processIntent();
             final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 0b56357..d3b23c2 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -191,6 +191,7 @@
     private final CallerInfoLookupHelper mCallerInfoLookupHelper;
     private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
     private final Timeouts.Adapter mTimeoutsAdapter;
+    private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
     /* Handler tied to thread in which CallManager was initialized. */
@@ -221,9 +222,11 @@
             SystemStateProvider systemStateProvider,
             DefaultDialerManagerAdapter defaultDialerAdapter,
             Timeouts.Adapter timeoutsAdapter,
-            AsyncRingtonePlayer asyncRingtonePlayer) {
+            AsyncRingtonePlayer asyncRingtonePlayer,
+            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
         mContext = context;
         mLock = lock;
+        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
         mContactsAsyncHelper = contactsAsyncHelper;
         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
@@ -669,6 +672,7 @@
                 mConnectionServiceRepository,
                 mContactsAsyncHelper,
                 mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
                 handle,
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
@@ -701,6 +705,7 @@
                 mConnectionServiceRepository,
                 mContactsAsyncHelper,
                 mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
                 handle,
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
@@ -773,6 +778,7 @@
                     mConnectionServiceRepository,
                     mContactsAsyncHelper,
                     mCallerInfoAsyncQueryFactory,
+                    mPhoneNumberUtilsAdapter,
                     handle,
                     null /* gatewayInfo */,
                     null /* connectionManagerPhoneAccount */,
@@ -1549,6 +1555,11 @@
         return getFirstCallWithState(null, states);
     }
 
+    @VisibleForTesting
+    public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() {
+        return mPhoneNumberUtilsAdapter;
+    }
+
     /**
      * Returns the first call that it finds with the given states. The states are treated as having
      * priority order so that any call with the first state will be returned before any call with
@@ -1606,6 +1617,7 @@
                 mConnectionServiceRepository,
                 mContactsAsyncHelper,
                 mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
                 null /* handle */,
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
@@ -1996,6 +2008,7 @@
                 mConnectionServiceRepository,
                 mContactsAsyncHelper,
                 mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
                 connection.getHandle() /* handle */,
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 5a58601..bf82a99 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -681,7 +681,7 @@
     }
 
     private final Adapter mAdapter = new Adapter();
-    private final CallIdMapper mCallIdMapper = new CallIdMapper();
+    private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
 
     private Binder2 mBinder = new Binder2();
@@ -731,6 +731,17 @@
         }
     }
 
+    /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
+    private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+        if (isServiceValid("removeConnectionServiceAdapter")) {
+            try {
+                logOutgoing("removeConnectionServiceAdapter %s", adapter);
+                mServiceInterface.removeConnectionServiceAdapter(adapter);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /**
      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
      */
@@ -1050,18 +1061,23 @@
     /** {@inheritDoc} */
     @Override
     protected void setServiceInterface(IBinder binder) {
-        if (binder == null) {
-            // We have lost our service connection. Notify the world that this service is done.
-            // We must notify the adapter before CallsManager. The adapter will force any pending
-            // outgoing calls to try the next service. This needs to happen before CallsManager
-            // tries to clean up any calls still associated with this service.
-            handleConnectionServiceDeath();
-            mCallsManager.handleConnectionServiceDeath(this);
-            mServiceInterface = null;
-        } else {
-            mServiceInterface = IConnectionService.Stub.asInterface(binder);
-            addConnectionServiceAdapter(mAdapter);
-        }
+        mServiceInterface = IConnectionService.Stub.asInterface(binder);
+        Log.v(this, "Adding Connection Service Adapter.");
+        addConnectionServiceAdapter(mAdapter);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void removeServiceInterface() {
+        Log.v(this, "Removing Connection Service Adapter.");
+        removeConnectionServiceAdapter(mAdapter);
+        // We have lost our service connection. Notify the world that this service is done.
+        // We must notify the adapter before CallsManager. The adapter will force any pending
+        // outgoing calls to try the next service. This needs to happen before CallsManager
+        // tries to clean up any calls still associated with this service.
+        handleConnectionServiceDeath();
+        mCallsManager.handleConnectionServiceDeath(this);
+        mServiceInterface = null;
     }
 
     private void handleCreateConnectionComplete(
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index c89a896..22ec3c6 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -96,6 +96,7 @@
     private final Context mContext;
     private CreateConnectionTimeout mTimeout;
     private ConnectionServiceWrapper mService;
+    private int mConnectionAttempt;
 
     @VisibleForTesting
     public CreateConnectionProcessor(
@@ -107,6 +108,7 @@
         mCallResponse = response;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mContext = context;
+        mConnectionAttempt = 0;
     }
 
     boolean isProcessingComplete() {
@@ -117,6 +119,10 @@
         return mTimeout != null && mTimeout.isCallTimedOut();
     }
 
+    public int getConnectionAttempt() {
+        return mConnectionAttempt;
+    }
+
     @VisibleForTesting
     public void process() {
         Log.v(this, "process");
@@ -200,6 +206,7 @@
                 Log.i(this, "Found no connection service for attempt %s", attempt);
                 attemptNextPhoneAccount();
             } else {
+                mConnectionAttempt++;
                 mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                 mCall.setConnectionService(mService);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index c6f42d2..1432373 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -589,7 +589,7 @@
      */
     private ComponentName mInCallUIComponentName;
 
-    private final CallIdMapper mCallIdMapper = new CallIdMapper();
+    private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
 
     /** The {@link ComponentName} of the default InCall UI. */
     private final ComponentName mSystemInCallComponentName;
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
index 41284cb..8e59a64 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
@@ -24,6 +24,7 @@
  * refactoring.
  */
 public interface PhoneNumberUtilsAdapter {
+    boolean isLocalEmergencyNumber(Context context, String number);
     boolean isPotentialLocalEmergencyNumber(Context context, String number);
     boolean isUriNumber(String number);
     String getNumberFromIntent(Intent intent, Context context);
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
index 640d814..fa316a5 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
@@ -22,6 +22,11 @@
 
 public class PhoneNumberUtilsAdapterImpl implements PhoneNumberUtilsAdapter {
     @Override
+    public boolean isLocalEmergencyNumber(Context context, String number) {
+            return PhoneNumberUtils.isLocalEmergencyNumber(context, number);
+    }
+
+    @Override
     public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
         return PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number);
     }
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index 18d6581..9a0f7b4 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -336,22 +336,28 @@
      */
     private void setBinder(IBinder binder) {
         if (mBinder != binder) {
-            mBinder = binder;
-
-            setServiceInterface(binder);
-
             if (binder == null) {
+                removeServiceInterface();
+                mBinder = null;
                 for (Listener l : mListeners) {
                     l.onUnbind(this);
                 }
+            } else {
+                mBinder = binder;
+                setServiceInterface(binder);
             }
         }
     }
 
     /**
-     * Sets the service interface after the service is bound or unbound.
+     * Sets the service interface after the service is bound.
      *
-     * @param binder The actual bound service implementation.
+     * @param binder The new binder interface that is being set.
      */
     protected abstract void setServiceInterface(IBinder binder);
+
+    /**
+     * Removes the service interface before the service is unbound.
+     */
+    protected abstract void removeServiceInterface();
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 304f13b..67321d4 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -154,7 +154,8 @@
             BluetoothPhoneServiceImplFactory
                     bluetoothPhoneServiceImplFactory,
             Timeouts.Adapter timeoutsAdapter,
-            AsyncRingtonePlayer asyncRingtonePlayer) {
+            AsyncRingtonePlayer asyncRingtonePlayer,
+            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
         mContext = context.getApplicationContext();
         Log.setContext(mContext);
         Log.initMd5Sum();
@@ -196,7 +197,8 @@
                 systemStateProvider,
                 defaultDialerAdapter,
                 timeoutsAdapter,
-                asyncRingtonePlayer);
+                asyncRingtonePlayer,
+                phoneNumberUtilsAdapter);
 
         mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
         mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index bef8453..a5790fd 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -36,6 +36,7 @@
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.Log;
@@ -148,7 +149,8 @@
                                 }
                             },
                             new Timeouts.Adapter(),
-                            new AsyncRingtonePlayer()
+                            new AsyncRingtonePlayer(),
+                            new PhoneNumberUtilsAdapterImpl()
                     ));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index f9d2793..1e97695 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -600,7 +600,7 @@
                                 // Convert the data to a call object
                                 Call call = new Call(Call.CALL_ID_UNKNOWN, mContext, callsManager,
                                         lock, null, contactsAsyncHelper,
-                                        callerInfoAsyncQueryFactory, null, null, null, null,
+                                        callerInfoAsyncQueryFactory, null, null, null, null, null,
                                         Call.CALL_DIRECTION_INCOMING, false, false);
                                 call.setDisconnectCause(
                                         new DisconnectCause(DisconnectCause.MISSED));
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 9211d05..ceec91b 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -122,7 +122,7 @@
         telecomManager.acceptRingingCall();
 
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .answer(ids.mCallId);
+                .answer(ids.mConnectionId);
         mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
 
         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -150,7 +150,7 @@
 
         // Answer video API should be called
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+                .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
         mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
 
         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -177,7 +177,7 @@
 
         // The generic answer method on the ConnectionService is used to answer audio-only calls.
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .answer(eq(ids.mCallId));
+                .answer(eq(ids.mConnectionId));
         mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
 
         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -205,7 +205,7 @@
 
         // Answer video API should be called
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+                .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
         mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
         mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
     }
@@ -643,7 +643,7 @@
 
         mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, null);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .sendCallEvent(ids.mCallId, TEST_EVENT, null);
+                .sendCallEvent(ids.mConnectionId, TEST_EVENT, null);
     }
 
     /**
@@ -664,7 +664,7 @@
         mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT,
                 testBundle);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .sendCallEvent(eq(ids.mCallId), eq(TEST_EVENT),
+                .sendCallEvent(eq(ids.mConnectionId), eq(TEST_EVENT),
                         bundleArgumentCaptor.capture());
         assert (bundleArgumentCaptor.getValue().containsKey(TEST_BUNDLE_KEY));
     }
@@ -762,7 +762,7 @@
         // Attempt to pull the call and verify the API call makes it through
         mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
-                .pullExternalCall(ids.mCallId);
+                .pullExternalCall(ids.mConnectionId);
     }
 
     /**
@@ -786,7 +786,7 @@
         // Attempt to pull the call and verify the API call makes it through
         mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT).never())
-                .pullExternalCall(ids.mCallId);
+                .pullExternalCall(ids.mConnectionId);
     }
 
     public void testMergeFailedAndNotifyInCallUi() throws Exception {
@@ -818,4 +818,24 @@
         verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT)).onConnectionEvent(
                 eq(testCall2.mCallId), eq(Connection.EVENT_CALL_MERGE_FAILED), any(Bundle.class));
     }
+
+    @LargeTest
+    public void testEmergencyCallFailMoveToSecondSim() throws Exception {
+        IdPair ids = startAndMakeDialingEmergencyCall("650-555-1212",
+                mPhoneAccountE0.getAccountHandle(), mConnectionServiceFixtureA);
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // The Emergency Call has failed on the default SIM with an ERROR Disconnect Cause. Retry
+        // with the other SIM PhoneAccount
+        IdPair newIds = triggerEmergencyRedial(mPhoneAccountE1.getAccountHandle(),
+                mConnectionServiceFixtureA, ids);
+
+        // Call should be active on the E1 PhoneAccount
+        mConnectionServiceFixtureA.sendSetActive(newIds.mConnectionId);
+        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(newIds.mCallId).getState());
+        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(newIds.mCallId).getState());
+        assertEquals(mInCallServiceFixtureX.getCall(ids.mCallId).getAccountHandle(),
+                mPhoneAccountE1.getAccountHandle());
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index fba4fb0..5ed941e 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -519,6 +519,9 @@
         for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
             ResolveInfo resolveInfo = new ResolveInfo();
             resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
+            resolveInfo.serviceInfo.metaData = new Bundle();
+            resolveInfo.serviceInfo.metaData.putBoolean(
+                    TelecomManager.METADATA_INCLUDE_EXTERNAL_CALLS, true);
             result.add(resolveInfo);
         }
         return result;
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 03ec2b2..962ab10 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -147,7 +147,7 @@
             capabilities |= CAPABILITY_HOLD;
             setVideoState(videoState);
             setConnectionCapabilities(capabilities);
-            setActive();
+            setDialing();
             setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
         }
 
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 7081aed..0173ed8 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -90,7 +90,7 @@
 
     private UserHandle mUserHandle = UserHandle.of(CURRENT_USER_ID);
     private InCallController mInCallController;
-    private TelecomSystem.SyncRoot mLock;
+    private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
 
     @Override
     public void setUp() throws Exception {
@@ -386,36 +386,6 @@
         assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName());
     }
 
-    /**
-     * Ensures that the {@link InCallController} will not bind to an incall service for an external
-     * call if the incall service does not support it.
-     */
-    @MediumTest
-    public void testBindToService_ExcludeExternal() throws Exception {
-        setupMocks(true /* isExternalCall */);
-        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
-        mInCallController.bindToServices(mMockCall);
-
-        // Query for the different InCallServices
-        ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
-                queryIntentCaptor.capture(),
-                eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
-
-        // Verify call for default dialer InCallService
-        assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
-        // Verify call for car-mode InCallService
-        assertEquals(null, queryIntentCaptor.getAllValues().get(1).getPackage());
-        // Verify call for non-UI InCallServices
-        assertEquals(null, queryIntentCaptor.getAllValues().get(2).getPackage());
-
-        verify(mMockContext, never()).bindServiceAsUser(
-                any(Intent.class),
-                any(ServiceConnection.class),
-                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
-                eq(UserHandle.CURRENT));
-    }
-
     private void setupMocks(boolean isExternalCall) {
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index fc8e1a5..9c47d3f 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -50,6 +50,7 @@
 import android.provider.BlockedNumberContract;
 import android.telecom.Call;
 import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -70,6 +71,8 @@
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.TelecomSystem;
@@ -145,6 +148,19 @@
     }
 
     MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
+    private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl {
+
+        @Override
+        public boolean isLocalEmergencyNumber(Context context, String number) {
+            return mIsEmergencyCall;
+        }
+
+        @Override
+        public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
+            return mIsEmergencyCall;
+        }
+    }
+    PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new EmergencyNumberUtilsAdapter();
     @Mock HeadsetMediaButton mHeadsetMediaButton;
     @Mock ProximitySensorManager mProximitySensorManager;
     @Mock InCallWakeLockController mInCallWakeLockController;
@@ -205,6 +221,31 @@
                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
                     .build();
+    final PhoneAccount mPhoneAccountE0 =
+            PhoneAccount.builder(
+                    new PhoneAccountHandle(
+                            mConnectionServiceComponentNameA,
+                            "id E 0"),
+                    "Phone account service E ID 0")
+                    .addSupportedUriScheme("tel")
+                    .setCapabilities(
+                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+                                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+                    .build();
+
+    final PhoneAccount mPhoneAccountE1 =
+            PhoneAccount.builder(
+                    new PhoneAccountHandle(
+                            mConnectionServiceComponentNameA,
+                            "id E 1"),
+                    "Phone account service E ID 1")
+                    .addSupportedUriScheme("tel")
+                    .setCapabilities(
+                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+                                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+                    .build();
 
     ConnectionServiceFixture mConnectionServiceFixtureA;
     ConnectionServiceFixture mConnectionServiceFixtureB;
@@ -220,6 +261,8 @@
 
     private int mNumOutgoingCallsMade;
 
+    private boolean mIsEmergencyCall;
+
     class IdPair {
         final String mConnectionId;
         final String mCallId;
@@ -238,6 +281,8 @@
 
         mNumOutgoingCallsMade = 0;
 
+        mIsEmergencyCall = false;
+
         // First set up information about the In-Call services in the mock Context, since
         // Telecom will search for these as soon as it is instantiated
         setupInCallServices();
@@ -327,7 +372,8 @@
                     }
                 },
                 mTimeoutsAdapter,
-                mAsyncRingtonePlayer);
+                mAsyncRingtonePlayer,
+                mPhoneNumberUtilsAdapter);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
@@ -357,6 +403,8 @@
         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
+        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0);
+        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1);
 
         mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
                 mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
@@ -460,10 +508,46 @@
                 phoneAccountHandle, connectionServiceFixture);
     }
 
-    protected String startOutgoingPhoneCallPendingCreateConnection(String number,
+    protected IdPair triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle,
+            ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds)
+            throws Exception {
+        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
+        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
+
+        // Send the message to disconnect the Emergency call due to an error.
+        // CreateConnectionProcessor should now try the second SIM account
+        connectionServiceFixture.sendSetDisconnected(emergencyIds.mConnectionId,
+                DisconnectCause.ERROR);
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(
+                emergencyIds.mCallId).getState());
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(
+                emergencyIds.mCallId).getState());
+
+        return redialingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
+                phoneAccountHandle, connectionServiceFixture);
+    }
+
+    protected IdPair startOutgoingEmergencyCall(String number,
             PhoneAccountHandle phoneAccountHandle,
             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
             int videoState) throws Exception {
+        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
+        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
+
+        mIsEmergencyCall = true;
+        // Call will not use the ordered broadcaster, since it is an Emergency Call
+        startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
+                connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
+
+        return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
+                phoneAccountHandle, connectionServiceFixture);
+    }
+
+    protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
+            PhoneAccountHandle phoneAccountHandle,
+            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
+            int videoState, boolean isEmergency) throws Exception {
         reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
                 mInCallServiceFixtureY.getTestDouble());
 
@@ -479,7 +563,11 @@
         Intent actionCallIntent = new Intent();
         actionCallIntent.setData(Uri.parse("tel:" + number));
         actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
-        actionCallIntent.setAction(Intent.ACTION_CALL);
+        if(isEmergency) {
+            actionCallIntent.setAction(Intent.ACTION_CALL_EMERGENCY);
+        } else {
+            actionCallIntent.setAction(Intent.ACTION_CALL);
+        }
         if (phoneAccountHandle != null) {
             actionCallIntent.putExtra(
                     TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
@@ -507,6 +595,14 @@
                     .setInCallAdapter(
                             any(IInCallAdapter.class));
         }
+    }
+
+    protected String startOutgoingPhoneCallPendingCreateConnection(String number,
+            PhoneAccountHandle phoneAccountHandle,
+            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
+            int videoState) throws Exception {
+        startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
+                connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
 
         ArgumentCaptor<Intent> newOutgoingCallIntent =
                 ArgumentCaptor.forClass(Intent.class);
@@ -538,6 +634,30 @@
         return mInCallServiceFixtureX.mLatestCallId;
     }
 
+    // When Telecom is redialing due to an error, we need to make sure the number of connections
+    // increase, but not the number of Calls in the InCallService.
+    protected IdPair redialingCallCreateConnectionComplete(int startingNumConnections,
+            int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
+            ConnectionServiceFixture connectionServiceFixture) throws Exception {
+
+        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
+
+        verify(connectionServiceFixture.getTestDouble())
+                .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
+                        eq(false)/*isIncoming*/, anyBoolean());
+        // Wait for handleCreateConnectionComplete
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Make sure the number of registered InCallService Calls stays the same.
+        assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size());
+        assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size());
+
+        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
+
+        return new IdPair(connectionServiceFixture.mLatestConnectionId,
+                mInCallServiceFixtureX.mLatestCallId);
+    }
+
     protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
             int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
             ConnectionServiceFixture connectionServiceFixture) throws Exception {
@@ -736,6 +856,20 @@
         return ids;
     }
 
+    protected IdPair startAndMakeDialingEmergencyCall(
+            String number,
+            PhoneAccountHandle phoneAccountHandle,
+            ConnectionServiceFixture connectionServiceFixture) throws Exception {
+        IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle,
+                connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
+
+        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        return ids;
+    }
+
     protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
         int elapsed = 0;
         while (elapsed < TEST_TIMEOUT) {