Integrate new ImsCallSessionListener am: 8409c97036 am: 4567efc2fc
am: 5f8a3671b1  -s ours

Change-Id: Ifa1216ae54e8a1977cff5022b0b43ac0a897391e
diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java
index 6200a07..e32103e 100644
--- a/src/java/com/android/ims/ImsCall.java
+++ b/src/java/com/android/ims/ImsCall.java
@@ -36,10 +36,15 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
 import android.util.Log;
 
 import com.android.ims.internal.ICall;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
 import com.android.ims.internal.ImsStreamMediaSession;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -1623,6 +1628,7 @@
             // Make a copy of the current ImsCallProfile and modify it to enable RTT
             Parcel p = Parcel.obtain();
             mCallProfile.writeToParcel(p, 0);
+            p.setDataPosition(0);
             ImsCallProfile requestedProfile = new ImsCallProfile(p);
             requestedProfile.mMediaProfile.setRttMode(ImsStreamMediaProfile.RTT_MODE_FULL);
 
@@ -3072,6 +3078,7 @@
         public void callSessionRttModifyRequestReceived(ImsCallSession session,
                 ImsCallProfile callProfile) {
             ImsCall.Listener listener;
+            logi("callSessionRttModifyRequestReceived");
 
             synchronized(ImsCall.this) {
                 listener = mListener;
@@ -3096,6 +3103,7 @@
         public void callSessionRttModifyResponseReceived(int status) {
             ImsCall.Listener listener;
 
+            logi("callSessionRttModifyResponseReceived");
             synchronized(ImsCall.this) {
                 listener = mListener;
             }
diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java
index 216bc41..d5fb633 100644
--- a/src/java/com/android/ims/ImsConnectionStateListener.java
+++ b/src/java/com/android/ims/ImsConnectionStateListener.java
@@ -17,8 +17,13 @@
 package com.android.ims;
 
 import android.net.Uri;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 
+import java.util.Arrays;
+
 /**
  * Listener for receiving notifications about changes to the IMS connection.
  * It provides a state of IMS registration between UE and IMS network, the service
@@ -53,6 +58,69 @@
     public void onSubscriberAssociatedUriChanged(Uri[] uris) {
         registrationAssociatedUriChanged(uris);
     }
+
+    /**
+     * Used to convert from the new capabilities structure to the old features structure for
+     * backwards compatibility.
+     * @param imsRadioTech The registration that will be used to convert to the old feature
+     *         structure. Can be either {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * @param c Capabilities that will be turned into old feature array.
+     */
+    public void onFeatureCapabilityChangedAdapter(
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+            ImsFeature.Capabilities c) {
+        // Size of ImsConfig.FeatureConstants
+        int[] enabledCapabilities = new int[6];
+        // UNKNOWN means disabled.
+        Arrays.fill(enabledCapabilities, ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN);
+        // Size of ImsConfig.FeatureConstants
+        int[] disabledCapabilities = new int[6];
+        Arrays.fill(disabledCapabilities, ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN);
+        // populate enabledCapabilities
+        switch (imsRadioTech) {
+            case ImsRegistrationImplBase.REGISTRATION_TECH_LTE: {
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)) {
+                    // enabled means equal to its own integer value.
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
+                }
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO)) {
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE;
+                }
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT)) {
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE;
+                }
+                break;
+            }
+            case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN: {
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)) {
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI;
+                }
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO)) {
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI;
+                }
+                if (c.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT)) {
+                    enabledCapabilities[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI] =
+                            ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI;
+                }
+                break;
+            }
+        }
+        // make disabledCapabilities the opposite of enabledCapabilities. Since the disabled
+        // capabilities array was defaulted to -1 it is UNKNOWN if not disabled.
+        for (int i = 0; i < enabledCapabilities.length; i++) {
+            if (enabledCapabilities[i] != i) {
+                disabledCapabilities[i] = i;
+            }
+        }
+        onFeatureCapabilityChanged(ImsServiceClass.MMTEL, enabledCapabilities,
+                disabledCapabilities);
+    }
     /**
      * Called when the device is connected to the IMS network with {@param imsRadioTech}.
      */
diff --git a/src/java/com/android/ims/ImsEcbm.java b/src/java/com/android/ims/ImsEcbm.java
index 53549bf..99d99f1 100644
--- a/src/java/com/android/ims/ImsEcbm.java
+++ b/src/java/com/android/ims/ImsEcbm.java
@@ -29,18 +29,12 @@
 
 package com.android.ims;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
 
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsEcbmListener;
-import com.android.ims.ImsEcbmStateListener;
 
 /**
  * Provides APIs for the supplementary service settings using IMS (Ut interface).
diff --git a/src/java/com/android/ims/ImsExternalCallStateListener.java b/src/java/com/android/ims/ImsExternalCallStateListener.java
index 3f4f163..aae4c9b 100644
--- a/src/java/com/android/ims/ImsExternalCallStateListener.java
+++ b/src/java/com/android/ims/ImsExternalCallStateListener.java
@@ -16,6 +16,8 @@
 
 package com.android.ims;
 
+import android.telephony.ims.ImsExternalCallState;
+
 import java.util.List;
 
 /**
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
index eaf973d..0a0eb24 100644
--- a/src/java/com/android/ims/ImsManager.java
+++ b/src/java/com/android/ims/ImsManager.java
@@ -16,51 +16,44 @@
 
 package com.android.ims;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
+import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.ims.internal.feature.ImsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.Rlog;
-import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
 import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsConfig;
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationCallback;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsSmsListener;
 import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.ExponentialBackoff;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.CopyOnWriteArraySet;
 
 /**
@@ -87,13 +80,13 @@
 
     /**
      * The result code to be sent back with the incoming call {@link PendingIntent}.
-     * @see #open(PendingIntent, ImsConnectionStateListener)
+     * @see #open(MmTelFeature.Listener)
      */
     public static final int INCOMING_CALL_RESULT_CODE = 101;
 
     /**
      * Key to retrieve the call ID from an incoming call intent.
-     * @see #open(PendingIntent, ImsConnectionStateListener)
+     * @see #open(MmTelFeature.Listener)
      */
     public static final String EXTRA_CALL_ID = "android:imsCallID";
 
@@ -168,6 +161,11 @@
      */
     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
 
+    private static final int SYSTEM_PROPERTY_NOT_SET = -1;
+
+    // -1 indicates a subscriptionProperty value that is never set.
+    private static final int SUB_PROPERTY_NOT_INITIALIZED = -1;
+
     private static final String TAG = "ImsManager";
     private static final boolean DBG = true;
 
@@ -178,7 +176,7 @@
     private CarrierConfigManager mConfigManager;
     private int mPhoneId;
     private final boolean mConfigDynamicBind;
-    private ImsServiceProxy mImsServiceProxy = null;
+    private @Nullable MmTelFeatureConnection mMmTelFeatureConnection = null;
     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
     // Ut interface for the supplementary service configuration
     private ImsUt mUt = null;
@@ -193,33 +191,8 @@
 
     private ImsMultiEndpoint mMultiEndpoint = null;
 
-    private Set<ImsServiceProxy.IFeatureUpdate> mStatusCallbacks = new CopyOnWriteArraySet<>();
-
-    // Keep track of the ImsRegistrationListenerProxys that have been created so that we can
-    // remove them from the ImsService.
-    private final Set<ImsConnectionStateListener> mRegistrationListeners = new HashSet<>();
-
-
-    // Used for compatibility with the old Registration method
-    // TODO: Remove once the compat layer is in place
-    private final ImsRegistrationListenerProxy mImsRegistrationListenerProxy =
-            new ImsRegistrationListenerProxy();
-    // New API for registration to the ImsService.
-    private final ImsRegistrationCallback mRegistrationCallback = new ImsRegistrationCallback();
-
-
-    // When true, we have registered the mRegistrationListenerProxy with the ImsService. Don't do
-    // it again.
-    private boolean mHasRegisteredForProxy = false;
-    private final Object mHasRegisteredLock = new Object();
-
-    // SystemProperties used as cache
-    private static final String VOLTE_PROVISIONED_PROP = "net.lte.ims.volte.provisioned";
-    private static final String WFC_PROVISIONED_PROP = "net.lte.ims.wfc.provisioned";
-    private static final String VT_PROVISIONED_PROP = "net.lte.ims.vt.provisioned";
-    // Flag indicating data enabled or not. This flag should be in sync with
-    // DcTracker.isDataEnabled(). The flag will be set later during boot up.
-    private static final String DATA_ENABLED_PROP = "net.lte.ims.data.enabled";
+    private Set<MmTelFeatureConnection.IFeatureUpdate> mStatusCallbacks =
+            new CopyOnWriteArraySet<>();
 
     public static final String TRUE = "true";
     public static final String FALSE = "false";
@@ -229,19 +202,6 @@
     private ConcurrentLinkedDeque<ImsReasonInfo> mRecentDisconnectReasons =
             new ConcurrentLinkedDeque<>();
 
-    // Exponential backoff for provisioning cache update. May be null for instances of ImsManager
-    // that are not on a thread supporting a looper.
-    private ExponentialBackoff mProvisionBackoff;
-    // Initial Provisioning check delay in ms
-    private static final long BACKOFF_INITIAL_DELAY_MS = 500;
-    // Max Provisioning check delay in ms (5 Minutes)
-    private static final long BACKOFF_MAX_DELAY_MS = 300000;
-    // Multiplier for exponential delay
-    private static final int BACKOFF_MULTIPLIER = 2;
-    // -1 indicates a subscriptionProperty value that is never set.
-    private static final int SUB_PROPERTY_NOT_INITIALIZED = -1;
-
-
     /**
      * Gets a manager instance.
      *
@@ -285,9 +245,12 @@
 
     /**
      * Returns the user configuration of Enhanced 4G LTE Mode setting for slot. If the option is
-     * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false),
-     * this method will return default value specified by
+     * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), or
+     * the setting is not initialized, this method will return default value specified by
      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+     *
+     * Note that even if the setting was set, it may no longer be editable. If this is the case we
+     * return the default value.
      */
     public boolean isEnhanced4gLteModeSettingEnabledByUser() {
         int setting = SubscriptionManager.getIntegerSubscriptionProperty(
@@ -407,8 +370,13 @@
      * basis.
      */
     public boolean isVolteEnabledByPlatform() {
-        if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
-                PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
+        // We first read the per slot value. If doesn't exist, we read the general value. If still
+        // doesn't exist, we use the hardcoded default value.
+        if (SystemProperties.getInt(
+                PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE + Integer.toString(mPhoneId),
+                SYSTEM_PROPERTY_NOT_SET) == 1 ||
+                SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
+                        SYSTEM_PROPERTY_NOT_SET) == 1) {
             return true;
         }
 
@@ -541,8 +509,12 @@
      * which must be done correctly).
      */
     public boolean isVtEnabledByPlatform() {
-        if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
-                PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
+        // We first read the per slot value. If doesn't exist, we read the general value. If still
+        // doesn't exist, we use the hardcoded default value.
+        if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE +
+                Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
+                SystemProperties.getInt(
+                        PROPERTY_DBG_VT_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
             return true;
         }
 
@@ -577,7 +549,8 @@
                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
 
         // If it's never set, by default we return true.
-        return (setting == SUB_PROPERTY_NOT_INITIALIZED || setting == 1);
+        return (setting == SUB_PROPERTY_NOT_INITIALIZED
+                || setting == ImsConfig.FeatureValueConstants.ON);
     }
 
     /**
@@ -598,16 +571,12 @@
      * Change persistent VT enabled setting for slot.
      */
     public void setVtSetting(boolean enabled) {
-        SubscriptionManager.setSubscriptionProperty(getSubId(),
-                SubscriptionManager.VT_IMS_ENABLED,
+        SubscriptionManager.setSubscriptionProperty(getSubId(), SubscriptionManager.VT_IMS_ENABLED,
                 booleanToPropertyString(enabled));
+
         try {
-            ImsConfig config = getConfigInterface();
-            config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
-                    TelephonyManager.NETWORK_TYPE_LTE,
-                    enabled ? ImsConfig.FeatureValueConstants.ON
-                            : ImsConfig.FeatureValueConstants.OFF,
-                    mImsConfigListener);
+            changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, enabled);
 
             if (enabled) {
                 log("setVtSetting(b) : turnOnIms");
@@ -618,7 +587,10 @@
                 log("setVtSetting(b) : imsServiceAllowTurnOff -> turnOffIms");
                 turnOffIms();
             }
-        } catch (ImsException e) {
+        } catch (ImsException | RemoteException e) {
+            // The ImsService is down. Since the SubscriptionManager already recorded the user's
+            // preference, it will be resent in updateImsServiceConfig when the ImsPhoneCallTracker
+            // reconnects.
             loge("setVtSetting(b): ", e);
         }
     }
@@ -645,10 +617,15 @@
      * The platform property may override the carrier config.
      */
     private boolean isTurnOffImsAllowedByPlatform() {
-        if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
-                PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
+        // We first read the per slot value. If doesn't exist, we read the general value. If still
+        // doesn't exist, we use the hardcoded default value.
+        if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE +
+                Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
+                SystemProperties.getInt(
+                        PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
             return true;
         }
+
         return getBooleanCarrierConfig(
                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
     }
@@ -683,7 +660,7 @@
             return getBooleanCarrierConfig(
                     CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL);
         } else {
-            return setting == 1;
+            return setting == ImsConfig.FeatureValueConstants.ON;
         }
     }
 
@@ -717,18 +694,13 @@
      * @param wfcMode The WFC preference if WFC is enabled
      */
     public void setWfcNonPersistent(boolean enabled, int wfcMode) {
-        int imsFeatureValue =
-                enabled ? ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF;
         // Force IMS to register over LTE when turning off WFC
         int imsWfcModeFeatureValue =
                 enabled ? wfcMode : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
 
         try {
-            ImsConfig config = getConfigInterface();
-            config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
-                    TelephonyManager.NETWORK_TYPE_IWLAN,
-                    imsFeatureValue,
-                    mImsConfigListener);
+            changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, enabled);
 
             if (enabled) {
                 log("setWfcSetting() : turnOnIms");
@@ -741,7 +713,7 @@
             }
 
             setWfcModeInternal(imsWfcModeFeatureValue);
-        } catch (ImsException e) {
+        } catch (ImsException | RemoteException e) {
             loge("setWfcSetting(): ", e);
         }
     }
@@ -915,7 +887,7 @@
             Thread thread = new Thread(new Runnable() {
                 public void run() {
                     try {
-                        imsManager.getConfigInterface().setProvisionedValue(
+                        imsManager.getConfigInterface().setConfig(
                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
                                 value);
                     } catch (ImsException e) {
@@ -931,7 +903,7 @@
         final int value = wfcMode;
         Thread thread = new Thread(() -> {
             try {
-                getConfigInterface().setProvisionedValue(
+                getConfigInterface().setConfig(
                         ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE, value);
             } catch (ImsException e) {
                 // do nothing
@@ -968,7 +940,7 @@
             return getBooleanCarrierConfig(
                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL);
         } else {
-            return (setting == 1);
+            return setting == ImsConfig.FeatureValueConstants.ON;
         }
     }
 
@@ -1001,7 +973,7 @@
                 : ImsConfig.FeatureValueConstants.OFF;
         Thread thread = new Thread(() -> {
             try {
-                getConfigInterface().setProvisionedValue(
+                getConfigInterface().setConfig(
                         ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING, value);
             } catch (ImsException e) {
                 // do nothing
@@ -1034,8 +1006,12 @@
      * configuration settings which must be done correctly).
      */
     public boolean isWfcEnabledByPlatform() {
-        if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
-                PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
+        // We first read the per slot value. If doesn't exist, we read the general value. If still
+        // doesn't exist, we use the hardcoded default value.
+        if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE +
+                Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
+                SystemProperties.getInt(
+                        PROPERTY_DBG_WFC_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
             return true;
         }
 
@@ -1070,116 +1046,28 @@
     }
 
     /**
-     * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
-     *
-     * We cannot register receiver in ImsManager because this would lead to resource leak.
-     * ImsManager can be created in different processes and it is not notified when that process
-     * is about to be terminated.
-     *
-     * @hide
-     * */
-    public static void onProvisionedValueChanged(Context context, int item, String value) {
-        if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
-        ImsManager mgr = ImsManager.getInstance(context,
-                SubscriptionManager.getDefaultVoicePhoneId());
-
-        switch (item) {
-            case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
-                mgr.setVolteProvisionedProperty(value.equals("1"));
-                if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
-                break;
-
-            case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
-                mgr.setWfcProvisionedProperty(value.equals("1"));
-                if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
-                break;
-
-            case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
-                mgr.setVtProvisionedProperty(value.equals("1"));
-                if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
-                break;
-
+     * Will return with config value or throw an ImsException if we receive an error from
+     * ImsConfig for that value.
+     */
+    private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
+        int value = config.getProvisionedValue(item);
+        if (value == ImsConfig.OperationStatusConstants.UNKNOWN) {
+            throw new ImsException("getProvisionedBool failed with error for item: " + item,
+                    ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
         }
-    }
-
-    private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Boolean> {
-        @Override
-        protected Boolean doInBackground(Void... params) {
-            // disable on any error
-            setVolteProvisionedProperty(false);
-            setWfcProvisionedProperty(false);
-            setVtProvisionedProperty(false);
-
-            try {
-                ImsConfig config = getConfigInterface();
-                if (config != null) {
-                    setVolteProvisionedProperty(getProvisionedBool(config,
-                            ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
-                    if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
-
-                    setWfcProvisionedProperty(getProvisionedBool(config,
-                            ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
-                    if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
-
-                    setVtProvisionedProperty(getProvisionedBool(config,
-                            ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
-                    if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
-
-                }
-            } catch (ImsException ie) {
-                Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        protected void onPostExecute(Boolean completed) {
-            if (mProvisionBackoff == null) {
-                return;
-            }
-            if (!completed) {
-                mProvisionBackoff.notifyFailed();
-            } else {
-                mProvisionBackoff.stop();
-            }
-        }
-
-        /**
-         * Will return with config value or throw an ImsException if we receive an error from
-         * ImsConfig for that value.
-         */
-        private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
-            int value = config.getProvisionedValue(item);
-            if (value == ImsConfig.FeatureValueConstants.ERROR) {
-                throw new ImsException("getProvisionedBool failed with error for item: " + item,
-                        ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
-            }
-            return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
-        }
-    }
-
-    // used internally only, use #updateProvisionedValues instead.
-    private void handleUpdateProvisionedValues() {
-        if (getBooleanCarrierConfig(
-                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
-
-            new AsyncUpdateProvisionedValues().execute();
-        }
+        return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
     }
 
     /**
-     * Asynchronously get VoLTE, WFC, VT provisioning statuses. If ImsConfig is not available, we
-     * will retry with exponential backoff.
+     * Will return with config value or return false if we receive an error from
+     * ImsConfig for that value.
      */
-    private void updateProvisionedValues() {
-        // Start trying to receive provisioning status after BACKOFF_INITIAL_DELAY_MS.
-        if (mProvisionBackoff != null) {
-            mProvisionBackoff.start();
-        } else {
-            // bypass and launch async thread once without backoff.
-            handleUpdateProvisionedValues();
+    private boolean getProvisionedBoolNoException(int item) {
+        try {
+            ImsConfig config = getConfigInterface();
+            return getProvisionedBool(config, item);
+        } catch (ImsException ex) {
+            return false;
         }
     }
 
@@ -1204,8 +1092,6 @@
     /**
      * Sync carrier config and user settings with ImsConfig.
      *
-     * @param context for the manager object
-     * @param phoneId phone id
      * @param force update
      */
     public void updateImsServiceConfig(boolean force) {
@@ -1220,13 +1106,11 @@
 
         if (!mConfigUpdated || force) {
             try {
-                updateProvisionedValues();
-
                 // TODO: Extend ImsConfig API and set all feature values in single function call.
 
                 // Note: currently the order of updates is set to produce different order of
-                // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
-                // differentiate this code path from vendor code perspective.
+                // changeEnabledCapabilities() function calls from setAdvanced4GMode(). This is done
+                // to differentiate this code path from vendor code perspective.
                 boolean isImsUsed = updateVolteFeatureValue();
                 isImsUsed |= updateWfcFeatureAndProvisionedValues();
                 isImsUsed |= updateVideoCallFeatureValue();
@@ -1245,7 +1129,7 @@
                 }
 
                 mConfigUpdated = true;
-            } catch (ImsException e) {
+            } catch (ImsException | RemoteException e) {
                 loge("updateImsServiceConfig: ", e);
                 mConfigUpdated = false;
             }
@@ -1257,7 +1141,7 @@
      * @return whether feature is On
      * @throws ImsException
      */
-    private boolean updateVolteFeatureValue() throws ImsException {
+    private boolean updateVolteFeatureValue() throws RemoteException {
         boolean available = isVolteEnabledByPlatform();
         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser();
         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
@@ -1267,13 +1151,8 @@
                 + ", enabled = " + enabled
                 + ", nonTTY = " + isNonTty);
 
-        getConfigInterface().setFeatureValue(
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
-                TelephonyManager.NETWORK_TYPE_LTE,
-                isFeatureOn ?
-                        ImsConfig.FeatureValueConstants.ON :
-                        ImsConfig.FeatureValueConstants.OFF,
-                mImsConfigListener);
+        changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isFeatureOn);
 
         return isFeatureOn;
     }
@@ -1283,7 +1162,7 @@
      * @return whether feature is On
      * @throws ImsException
      */
-    private boolean updateVideoCallFeatureValue() throws ImsException {
+    private boolean updateVideoCallFeatureValue() throws RemoteException {
         boolean available = isVtEnabledByPlatform();
         boolean enabled = isVtEnabledByUser();
         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
@@ -1299,13 +1178,8 @@
                 + ", nonTTY = " + isNonTty
                 + ", data enabled = " + isDataEnabled);
 
-        getConfigInterface().setFeatureValue(
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
-                TelephonyManager.NETWORK_TYPE_LTE,
-                isFeatureOn ?
-                        ImsConfig.FeatureValueConstants.ON :
-                        ImsConfig.FeatureValueConstants.OFF,
-                mImsConfigListener);
+        changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isFeatureOn);
 
         return isFeatureOn;
     }
@@ -1315,7 +1189,7 @@
      * @return whether feature is On
      * @throws ImsException
      */
-    private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
+    private boolean updateWfcFeatureAndProvisionedValues() throws RemoteException {
         TelephonyManager tm = new TelephonyManager(mContext, getSubId());
         boolean isNetworkRoaming = tm.isNetworkRoaming();
         boolean available = isWfcEnabledByPlatform();
@@ -1330,13 +1204,8 @@
                 + ", mode = " + mode
                 + ", roaming = " + roaming);
 
-        getConfigInterface().setFeatureValue(
-                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
-                TelephonyManager.NETWORK_TYPE_IWLAN,
-                isFeatureOn ?
-                        ImsConfig.FeatureValueConstants.ON :
-                        ImsConfig.FeatureValueConstants.OFF,
-                mImsConfigListener);
+        changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, isFeatureOn);
 
         if (!isFeatureOn) {
             mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
@@ -1359,11 +1228,6 @@
                 com.android.internal.R.bool.config_dynamic_bind_ims);
         mConfigManager = (CarrierConfigManager) context.getSystemService(
                 Context.CARRIER_CONFIG_SERVICE);
-        if (Looper.getMainLooper() != null) {
-            mProvisionBackoff = new ExponentialBackoff(BACKOFF_INITIAL_DELAY_MS,
-                    BACKOFF_MAX_DELAY_MS, BACKOFF_MULTIPLIER,
-                    new Handler(Looper.getMainLooper()), this::handleUpdateProvisionedValues);
-        }
         createImsService();
     }
 
@@ -1382,7 +1246,7 @@
     public boolean isServiceAvailable() {
         connectIfServiceIsAvailable();
         // mImsServiceProxy will always create an ImsServiceProxy.
-        return mImsServiceProxy.isBinderAlive();
+        return mMmTelFeatureConnection.isBinderAlive();
     }
 
     /*
@@ -1390,19 +1254,19 @@
      */
     public boolean isServiceReady() {
         connectIfServiceIsAvailable();
-        return mImsServiceProxy.isBinderReady();
+        return mMmTelFeatureConnection.isBinderReady();
     }
 
     /**
      * If the service is available, try to reconnect.
      */
     public void connectIfServiceIsAvailable() {
-        if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
+        if (mMmTelFeatureConnection == null || !mMmTelFeatureConnection.isBinderAlive()) {
             createImsService();
         }
     }
 
-    public void setImsConfigListener(ImsConfigListener listener) {
+    public void setConfigListener(ImsConfigListener listener) {
         mImsConfigListener = listener;
     }
 
@@ -1411,9 +1275,9 @@
      * Adds a callback for status changed events if the binder is already available. If it is not,
      * this method will throw an ImsException.
      */
-    public void addNotifyStatusChangedCallbackIfAvailable(ImsServiceProxy.IFeatureUpdate c)
+    public void addNotifyStatusChangedCallbackIfAvailable(MmTelFeatureConnection.IFeatureUpdate c)
             throws ImsException {
-        if (!mImsServiceProxy.isBinderAlive()) {
+        if (!mMmTelFeatureConnection.isBinderAlive()) {
             throw new ImsException("Binder is not active!",
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
@@ -1422,7 +1286,7 @@
         }
     }
 
-    public void removeNotifyStatusChangedCallback(ImsServiceProxy.IFeatureUpdate c) {
+    public void removeNotifyStatusChangedCallback(MmTelFeatureConnection.IFeatureUpdate c) {
         if (c != null) {
             mStatusCallbacks.remove(c);
         } else {
@@ -1432,63 +1296,30 @@
 
     /**
      * Opens the IMS service for making calls and/or receiving generic IMS calls.
-     * The caller may make subsquent calls through {@link #makeCall}.
+     * The caller may make subsequent calls through {@link #makeCall}.
      * The IMS service will register the device to the operator's network with the credentials
      * (from ISIM) periodically in order to receive calls from the operator's network.
-     * When the IMS service receives a new call, it will send out an intent with
-     * the provided action string.
-     * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
-     *
-     * @param serviceClass a service class specified in {@link ImsServiceClass}
-     *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
-     * @param incomingCallPendingIntent When an incoming call is received,
-     *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
-     *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
-     *        as the result code and the intent to fill in the call ID; It cannot be null
-     * @param listener To listen to IMS registration events; It cannot be null
-     * @return identifier (greater than 0) for the specified service
-     * @throws NullPointerException if {@code incomingCallPendingIntent}
-     *      or {@code listener} is null
+     * When the IMS service receives a new call, it will call
+     * {@link MmTelFeature.Listener#onIncomingCall}
+     * The listener contains a call ID extra {@link #getCallId} and it can be used to take a call.
+     * @param listener A {@link MmTelFeature.Listener}, which is the interface the
+     * {@link MmTelFeature} uses to notify the framework of updates
+     * @throws NullPointerException if {@code listener} is null
      * @throws ImsException if calling the IMS service results in an error
      * @see #getCallId
-     * @see #getImsSessionId
      */
-    public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
-            ImsConnectionStateListener listener) throws ImsException {
+    public void open(MmTelFeature.Listener listener) throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
-        if (incomingCallPendingIntent == null) {
-            throw new NullPointerException("incomingCallPendingIntent can't be null");
-        }
-
         if (listener == null) {
             throw new NullPointerException("listener can't be null");
         }
 
-        int result = 0;
-
         try {
-            // Register a stub implementation of the ImsRegistrationListener. There is the
-            // possibility that if we use the real implementation of the ImsRegistrationListener,
-            // it will be added twice.
-            // TODO: Remove ImsRegistrationListener from startSession API (b/62588776)
-            result = mImsServiceProxy.startSession(incomingCallPendingIntent,
-                    new ImsRegistrationListenerBase());
-            addRegistrationListener(listener);
-            log("open: Session started and registration listener added.");
+            mMmTelFeatureConnection.openConnection(listener);
         } catch (RemoteException e) {
-            throw new ImsException("open()", e,
-                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+            throw new ImsException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
-
-        if (result <= 0) {
-            // If the return value is a minus value,
-            // it means that an error occurred in the service.
-            // So, it needs to convert to the reason code specified in ImsReasonInfo.
-            throw new ImsException("open()", (result * (-1)));
-        }
-
-        return result;
     }
 
     /**
@@ -1513,34 +1344,68 @@
      * @param listener To listen to IMS registration events; It cannot be null
      * @throws NullPointerException if {@code listener} is null
      * @throws ImsException if calling the IMS service results in an error
+     * @deprecated use {@link #addRegistrationCallback(ImsRegistrationImplBase.Callback)} and
+     * {@link #addCapabilitiesCallback(ImsFeature.CapabilityCallback)} instead.
      */
     public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException {
         if (listener == null) {
             throw new NullPointerException("listener can't be null");
         }
-        // We only want this Proxy registered once.
-        synchronized (mHasRegisteredLock) {
-            if (!mHasRegisteredForProxy) {
-                try {
-                    checkAndThrowExceptionIfServiceUnavailable();
-                    // TODO: Remove once new MmTelFeature is merged in
-                    mImsServiceProxy.addRegistrationListener(mImsRegistrationListenerProxy);
-                    IImsRegistration regBinder = mImsServiceProxy.getRegistration();
-                    if (regBinder != null) {
-                        regBinder.addRegistrationCallback(mRegistrationCallback);
-                    }
-                    log("Registration Callback/Listener registered.");
-                    // Only record if there isn't a RemoteException.
-                    mHasRegisteredForProxy = true;
-                } catch (RemoteException e) {
-                    throw new ImsException("addRegistrationListener()", e,
-                            ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-                }
+        addRegistrationCallback(listener);
+        // connect the ImsConnectionStateListener to the new CapabilityCallback.
+        addCapabilitiesCallback(new ImsFeature.CapabilityCallback() {
+            @Override
+            public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+                listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), config);
             }
+        });
+        log("Registration Callback registered.");
+    }
+
+    /**
+     * Adds a callback that gets called when IMS registration has changed.
+     * @param callback A {@link ImsRegistrationImplBase.Callback} that will notify the caller when
+     *         IMS registration status has changed.
+     * @throws ImsException when the ImsService connection is not available.
+     */
+    public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+            throws ImsException {
+        if (callback == null) {
+            throw new NullPointerException("registration callback can't be null");
         }
-        synchronized (mRegistrationListeners) {
-            log("Local registration listener added: " + listener);
-            mRegistrationListeners.add(listener);
+
+        checkAndThrowExceptionIfServiceUnavailable();
+        try {
+            mMmTelFeatureConnection.addRegistrationCallback(callback);
+            log("Registration Callback registered.");
+            // Only record if there isn't a RemoteException.
+        } catch (RemoteException e) {
+            throw new ImsException("addRegistrationCallback(IRIB)", e,
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+        }
+    }
+
+    /**
+     * Adds a callback that gets called when MMTel capability status has changed, for example when
+     * Voice over IMS or VT over IMS is not available currently.
+     * @param callback A {@link ImsFeature.CapabilityCallback} that will notify the caller when
+     *         MMTel capability status has changed.
+     * @throws ImsException when the ImsService connection is not available.
+     */
+    public void addCapabilitiesCallback(ImsFeature.CapabilityCallback callback)
+            throws ImsException {
+        if (callback == null) {
+            throw new NullPointerException("capabilities callback can't be null");
+        }
+
+        checkAndThrowExceptionIfServiceUnavailable();
+        try {
+            mMmTelFeatureConnection.addCapabilityCallback(callback);
+            log("Capability Callback registered.");
+            // Only record if there isn't a RemoteException.
+        } catch (RemoteException e) {
+            throw new ImsException("addCapabilitiesCallback(IF)", e,
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
     }
 
@@ -1558,33 +1423,38 @@
             throw new NullPointerException("listener can't be null");
         }
 
-        synchronized (mRegistrationListeners) {
-            log("Local registration listener removed: " + listener);
-            mRegistrationListeners.remove(listener);
+        checkAndThrowExceptionIfServiceUnavailable();
+        try {
+            mMmTelFeatureConnection.removeRegistrationCallback(listener);
+            log("Registration Callback/Listener registered.");
+            // Only record if there isn't a RemoteException.
+        } catch (RemoteException e) {
+            throw new ImsException("addRegistrationCallback()", e,
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+        }
+    }
+
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() {
+        try {
+            return mMmTelFeatureConnection.getRegistrationTech();
+        } catch (RemoteException e) {
+            Log.w(TAG, "getRegistrationTech: no connection to ImsService.");
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
         }
     }
 
     /**
-     * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
+     * Closes the connection and removes all active callbacks.
      * All the resources that were allocated to the service are also released.
-     *
-     * @param sessionId a session id to be closed which is obtained from {@link ImsManager#open}
-     * @throws ImsException if calling the IMS service results in an error
      */
-    public void close(int sessionId) throws ImsException {
-        checkAndThrowExceptionIfServiceUnavailable();
-
-        try {
-            mImsServiceProxy.endSession(sessionId);
-        } catch (RemoteException e) {
-            throw new ImsException("close()", e,
-                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-        } finally {
-            mUt = null;
-            mConfig = null;
-            mEcbm = null;
-            mMultiEndpoint = null;
+    public void close() {
+        if (mMmTelFeatureConnection != null) {
+            mMmTelFeatureConnection.closeConnection();
         }
+        mUt = null;
+        mConfig = null;
+        mEcbm = null;
+        mMultiEndpoint = null;
     }
 
     /**
@@ -1593,8 +1463,7 @@
      * @return the Ut interface instance
      * @throws ImsException if getting the Ut interface results in an error
      */
-    public ImsUtInterface getSupplementaryServiceConfiguration()
-            throws ImsException {
+    public ImsUtInterface getSupplementaryServiceConfiguration() throws ImsException {
         // FIXME: manage the multiple Ut interfaces based on the session id
         if (mUt != null && mUt.isBinderAlive()) {
             return mUt;
@@ -1602,7 +1471,7 @@
 
         checkAndThrowExceptionIfServiceUnavailable();
         try {
-            IImsUt iUt = mImsServiceProxy.getUtInterface();
+            IImsUt iUt = mMmTelFeatureConnection.getUtInterface();
 
             if (iUt == null) {
                 throw new ImsException("getSupplementaryServiceConfiguration()",
@@ -1618,54 +1487,8 @@
     }
 
     /**
-     * Checks if the IMS service has successfully registered to the IMS network
-     * with the specified service & call type.
-     *
-     * @param serviceType a service type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
-     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
-     * @param callType a call type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     * @return true if the specified service id is connected to the IMS network;
-     *        false otherwise
-     * @throws ImsException if calling the IMS service results in an error
-     */
-    public boolean isConnected(int serviceType, int callType)
-            throws ImsException {
-        checkAndThrowExceptionIfServiceUnavailable();
-
-        try {
-            return mImsServiceProxy.isConnected(serviceType, callType);
-        } catch (RemoteException e) {
-            throw new ImsException("isServiceConnected()", e,
-                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-        }
-    }
-
-    /**
-     * Checks if the specified IMS service is opend.
-     *
-     * @return true if the specified service id is opened; false otherwise
-     * @throws ImsException if calling the IMS service results in an error
-     */
-    public boolean isOpened() throws ImsException {
-        checkAndThrowExceptionIfServiceUnavailable();
-
-        try {
-            return mImsServiceProxy.isOpened();
-        } catch (RemoteException e) {
-            throw new ImsException("isOpened()", e,
-                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-        }
-    }
-
-    /**
      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
      *
-     * @param sessionId a session id which is obtained from {@link ImsManager#open}
      * @param serviceType a service type that is specified in {@link ImsCallProfile}
      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
@@ -1682,12 +1505,11 @@
      * @return a {@link ImsCallProfile} object
      * @throws ImsException if calling the IMS service results in an error
      */
-    public ImsCallProfile createCallProfile(int sessionId, int serviceType, int callType)
-            throws ImsException {
+    public ImsCallProfile createCallProfile(int serviceType, int callType) throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            return mImsServiceProxy.createCallProfile(sessionId, serviceType, callType);
+            return mMmTelFeatureConnection.createCallProfile(serviceType, callType);
         } catch (RemoteException e) {
             throw new ImsException("createCallProfile()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1697,19 +1519,17 @@
     /**
      * Creates a {@link ImsCall} to make a call.
      *
-     * @param sessionId a session id which is obtained from {@link ImsManager#open}
      * @param profile a call profile to make the call
      *      (it contains service type, call type, media information, etc.)
-     * @param participants participants to invite the conference call
+     * @param callees participants to invite the conference call
      * @param listener listen to the call events from {@link ImsCall}
      * @return a {@link ImsCall} object
      * @throws ImsException if calling the IMS service results in an error
      */
-    public ImsCall makeCall(int sessionId, ImsCallProfile profile, String[] callees,
+    public ImsCall makeCall(ImsCallProfile profile, String[] callees,
             ImsCall.Listener listener) throws ImsException {
         if (DBG) {
-            log("makeCall :: sessionId=" + sessionId
-                    + ", profile=" + profile);
+            log("makeCall :: profile=" + profile);
         }
 
         checkAndThrowExceptionIfServiceUnavailable();
@@ -1717,7 +1537,7 @@
         ImsCall call = new ImsCall(mContext, profile);
 
         call.setListener(listener);
-        ImsCallSession session = createCallSession(sessionId, profile);
+        ImsCallSession session = createCallSession(profile);
 
         if ((callees != null) && (callees.length == 1)) {
             call.start(session, callees[0]);
@@ -1732,33 +1552,25 @@
      * Creates a {@link ImsCall} to take an incoming call.
      *
      * @param sessionId a session id which is obtained from {@link ImsManager#open}
-     * @param incomingCallIntent the incoming call broadcast intent
+     * @param incomingCallExtras the incoming call broadcast intent
      * @param listener to listen to the call events from {@link ImsCall}
      * @return a {@link ImsCall} object
      * @throws ImsException if calling the IMS service results in an error
      */
-    public ImsCall takeCall(int sessionId, Intent incomingCallIntent,
+    public ImsCall takeCall(IImsCallSession session, Bundle incomingCallExtras,
             ImsCall.Listener listener) throws ImsException {
         if (DBG) {
-            log("takeCall :: sessionId=" + sessionId
-                    + ", incomingCall=" + incomingCallIntent);
+            log("takeCall :: incomingCall=" + incomingCallExtras);
         }
 
         checkAndThrowExceptionIfServiceUnavailable();
 
-        if (incomingCallIntent == null) {
+        if (incomingCallExtras == null) {
             throw new ImsException("Can't retrieve session with null intent",
                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
         }
 
-        int incomingServiceId = getImsSessionId(incomingCallIntent);
-
-        if (sessionId != incomingServiceId) {
-            throw new ImsException("Service id is mismatched in the incoming call intent",
-                    ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
-        }
-
-        String callId = getCallId(incomingCallIntent);
+        String callId = getCallId(incomingCallExtras);
 
         if (callId == null) {
             throw new ImsException("Call ID missing in the incoming call intent",
@@ -1766,8 +1578,6 @@
         }
 
         try {
-            IImsCallSession session = mImsServiceProxy.getPendingCallSession(sessionId, callId);
-
             if (session == null) {
                 throw new ImsException("No pending session for the call",
                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
@@ -1797,7 +1607,7 @@
 
         checkAndThrowExceptionIfServiceUnavailable();
         try {
-            IImsConfig config = mImsServiceProxy.getConfigInterface();
+            IImsConfig config = mMmTelFeatureConnection.getConfigInterface();
             if (config == null) {
                 throw new ImsException("getConfigInterface()",
                         ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
@@ -1810,6 +1620,47 @@
         return mConfig;
     }
 
+    public void changeMmTelCapability(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech,
+            boolean isEnabled) throws RemoteException {
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        if (isEnabled) {
+            request.addCapabilitiesToEnableForTech(capability, radioTech);
+        } else {
+            request.addCapabilitiesToDisableForTech(capability, radioTech);
+        }
+        mMmTelFeatureConnection.changeEnabledCapabilities(request, null);
+        if (mImsConfigListener != null) {
+            mImsConfigListener.onSetFeatureResponse(capability,
+                    mMmTelFeatureConnection.getRegistrationTech(),
+                    isEnabled ? ImsConfig.FeatureValueConstants.ON
+                            : ImsConfig.FeatureValueConstants.OFF, -1);
+        }
+    }
+
+    public void setRttEnabled(boolean enabled) {
+        try {
+            setAdvanced4GMode(enabled || isEnhanced4gLteModeSettingEnabledByUser());
+            final int value = enabled ? ImsConfig.FeatureValueConstants.ON :
+                    ImsConfig.FeatureValueConstants.OFF;
+            Thread thread = new Thread(() -> {
+                try {
+                    Log.i(ImsManager.class.getSimpleName(), "Setting RTT enabled to " + enabled);
+                    getConfigInterface().setProvisionedValue(
+                            ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value);
+                } catch (ImsException e) {
+                    Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT enabled to "
+                            + enabled + ": " + e);
+                }
+            });
+            thread.start();
+        } catch (ImsException e) {
+            Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT enabled to " + enabled
+                    + ": " + e);
+        }
+    }
+
     /**
      * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
      */
@@ -1831,7 +1682,7 @@
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            mImsServiceProxy.setUiTTYMode(uiTtyMode, onComplete);
+            mMmTelFeatureConnection.setUiTTYMode(uiTtyMode, onComplete);
         } catch (RemoteException e) {
             throw new ImsException("setTTYMode()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1863,8 +1714,8 @@
         return disconnectReasons;
     }
 
-    public int getImsServiceStatus() throws ImsException {
-        return mImsServiceProxy.getFeatureStatus();
+    public int getImsServiceState() throws ImsException {
+        return mMmTelFeatureConnection.getFeatureState();
     }
 
     /**
@@ -1920,29 +1771,15 @@
     /**
      * Gets the call ID from the specified incoming call broadcast intent.
      *
-     * @param incomingCallIntent the incoming call broadcast intent
+     * @param incomingCallExtras the incoming call broadcast intent
      * @return the call ID or null if the intent does not contain it
      */
-    private static String getCallId(Intent incomingCallIntent) {
-        if (incomingCallIntent == null) {
+    private static String getCallId(Bundle incomingCallExtras) {
+        if (incomingCallExtras == null) {
             return null;
         }
 
-        return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
-    }
-
-    /**
-     * Gets the service type from the specified incoming call broadcast intent.
-     *
-     * @param incomingCallIntent the incoming call broadcast intent
-     * @return the session identifier or -1 if the intent does not contain it
-     */
-    private static int getImsSessionId(Intent incomingCallIntent) {
-        if (incomingCallIntent == null) {
-            return (-1);
-        }
-
-        return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
+        return incomingCallExtras.getString(EXTRA_CALL_ID);
     }
 
     /**
@@ -1951,10 +1788,10 @@
      */
     private void checkAndThrowExceptionIfServiceUnavailable()
             throws ImsException {
-        if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
+        if (mMmTelFeatureConnection == null || !mMmTelFeatureConnection.isBinderAlive()) {
             createImsService();
 
-            if (mImsServiceProxy == null) {
+            if (mMmTelFeatureConnection == null) {
                 throw new ImsException("Service is unavailable",
                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
             }
@@ -1968,30 +1805,21 @@
      * 2) android.telephony.ims.ImsService implementation through ImsResolver.
      */
     private void createImsService() {
-        if (!mConfigDynamicBind) {
-            // Deprecated method of binding
-            Rlog.i(TAG, "Creating ImsService using ServiceManager");
-            mImsServiceProxy = ImsServiceProxyCompat.create(mContext, mPhoneId, mDeathRecipient);
-        } else {
-            Rlog.i(TAG, "Creating ImsService using ImsResolver");
-            mImsServiceProxy = ImsServiceProxy.create(mContext, mPhoneId);
-        }
+        Rlog.i(TAG, "Creating ImsService");
+        mMmTelFeatureConnection = MmTelFeatureConnection.create(mContext, mPhoneId);
+
         // Forwarding interface to tell mStatusCallbacks that the Proxy is unavailable.
-        mImsServiceProxy.setStatusCallback(new ImsServiceProxy.IFeatureUpdate() {
+        mMmTelFeatureConnection.setStatusCallback(new MmTelFeatureConnection.IFeatureUpdate() {
             @Override
             public void notifyStateChanged() {
-                mStatusCallbacks.forEach(ImsServiceProxy.IFeatureUpdate::notifyStateChanged);
+                mStatusCallbacks.forEach(MmTelFeatureConnection.IFeatureUpdate::notifyStateChanged);
             }
 
             @Override
             public void notifyUnavailable() {
-                mStatusCallbacks.forEach(ImsServiceProxy.IFeatureUpdate::notifyUnavailable);
+                mStatusCallbacks.forEach(MmTelFeatureConnection.IFeatureUpdate::notifyUnavailable);
             }
         });
-        // We have created a new ImsService connection, signal for re-registration
-        synchronized (mHasRegisteredLock) {
-            mHasRegisteredForProxy = false;
-        }
     }
 
     /**
@@ -1999,14 +1827,12 @@
      * Use other methods, if applicable, instead of interacting with
      * {@link ImsCallSession} directly.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
      * @param profile a call profile to make the call
      */
-    private ImsCallSession createCallSession(int serviceId,
-            ImsCallProfile profile) throws ImsException {
+    private ImsCallSession createCallSession(ImsCallProfile profile) throws ImsException {
         try {
             // Throws an exception if the ImsService Feature is not ready to accept commands.
-            return new ImsCallSession(mImsServiceProxy.createCallSession(serviceId, profile));
+            return new ImsCallSession(mMmTelFeatureConnection.createCallSession(profile));
         } catch (RemoteException e) {
             Rlog.w(TAG, "CreateCallSession: Error, remote exception: " + e.getMessage());
             throw new ImsException("createCallSession()", e,
@@ -2031,13 +1857,9 @@
      * Used for turning on IMS.if its off already
      */
     private void turnOnIms() throws ImsException {
-        checkAndThrowExceptionIfServiceUnavailable();
-
-        try {
-            mImsServiceProxy.turnOnIms();
-        } catch (RemoteException e) {
-            throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-        }
+        TelephonyManager tm = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        tm.enableIms(mPhoneId);
     }
 
     private boolean isImsTurnOffAllowed() {
@@ -2048,25 +1870,36 @@
 
     private void setLteFeatureValues(boolean turnOn) {
         log("setLteFeatureValues: " + turnOn);
-        try {
-            ImsConfig config = getConfigInterface();
-            if (config != null) {
-                config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
-                        TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
+        CapabilityChangeRequest request = new CapabilityChangeRequest();
+        if (turnOn) {
+            request.addCapabilitiesToEnableForTech(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        } else {
+            request.addCapabilitiesToDisableForTech(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        }
 
-                if (isVolteEnabledByPlatform()) {
-                    boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(
-                            CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
-                    boolean enableViLte = turnOn && isVtEnabledByUser() &&
-                            (ignoreDataEnabledChanged || isDataEnabled());
-                    config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
-                            TelephonyManager.NETWORK_TYPE_LTE,
-                            enableViLte ? 1 : 0,
-                            mImsConfigListener);
-                }
+        if (isVolteEnabledByPlatform()) {
+            boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(
+                    CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+            boolean enableViLte = turnOn && isVtEnabledByUser() &&
+                    (ignoreDataEnabledChanged || isDataEnabled());
+            if (enableViLte) {
+                request.addCapabilitiesToEnableForTech(
+                        MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+            } else {
+                request.addCapabilitiesToDisableForTech(
+                        MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                        ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             }
-        } catch (ImsException e) {
-            loge("setLteFeatureValues: exception ", e);
+        }
+        try {
+            mMmTelFeatureConnection.changeEnabledCapabilities(request, null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "setLteFeatureValues: Exception: " + e.getMessage());
         }
     }
 
@@ -2094,13 +1927,9 @@
      * Once turned off, all calls will be over CS.
      */
     private void turnOffIms() throws ImsException {
-        checkAndThrowExceptionIfServiceUnavailable();
-
-        try {
-            mImsServiceProxy.turnOffIms();
-        } catch (RemoteException e) {
-            throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
-        }
+        TelephonyManager tm = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        tm.disableIms(mPhoneId);
     }
 
     private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
@@ -2117,7 +1946,7 @@
     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
-            mImsServiceProxy = null;
+            mMmTelFeatureConnection = null;
             mUt = null;
             mConfig = null;
             mEcbm = null;
@@ -2126,270 +1955,19 @@
     }
 
     /**
-     * Stub implementation of the Registration listener that provides no functionality.
-     */
-    private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub {
-
-        @Override
-        public void registrationConnected() throws RemoteException {
-        }
-
-        @Override
-        public void registrationProgressing() throws RemoteException {
-        }
-
-        @Override
-        public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
-        }
-
-        @Override
-        public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
-        }
-
-        @Override
-        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
-        }
-
-        @Override
-        public void registrationResumed() throws RemoteException {
-        }
-
-        @Override
-        public void registrationSuspended() throws RemoteException {
-        }
-
-        @Override
-        public void registrationServiceCapabilityChanged(int serviceClass, int event)
-                throws RemoteException {
-        }
-
-        @Override
-        public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
-                int[] disabledFeatures) throws RemoteException {
-        }
-
-        @Override
-        public void voiceMessageCountUpdate(int count) throws RemoteException {
-        }
-
-        @Override
-        public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
-        }
-
-        @Override
-        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
-                throws RemoteException {
-        }
-    }
-
-    /**
-     * Adapter class for {@link IImsRegistrationListener}.
-     */
-    private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
-
-        @Deprecated
-        public void registrationConnected() {
-            if (DBG) {
-                log("registrationConnected ::");
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsConnected(
-                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
-            }
-        }
-
-        @Deprecated
-        public void registrationProgressing() {
-            if (DBG) {
-                log("registrationProgressing ::");
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsProgressing(
-                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
-            }
-        }
-
-        @Override
-        public void registrationConnectedWithRadioTech(int imsRadioTech) {
-            // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
-            //       values in ServiceState.java.
-            if (DBG) {
-                log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsConnected(imsRadioTech));
-            }
-        }
-
-        @Override
-        public void registrationProgressingWithRadioTech(int imsRadioTech) {
-            // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
-            //       values in ServiceState.java.
-            if (DBG) {
-                log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsProgressing(imsRadioTech));
-            }
-        }
-
-        @Override
-        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
-            if (DBG) {
-                log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
-            }
-
-            addToRecentDisconnectReasons(imsReasonInfo);
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsDisconnected(imsReasonInfo));
-            }
-        }
-
-        @Override
-        public void registrationResumed() {
-            if (DBG) {
-                log("registrationResumed ::");
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(ImsConnectionStateListener::onImsResumed);
-            }
-        }
-
-        @Override
-        public void registrationSuspended() {
-            if (DBG) {
-                log("registrationSuspended ::");
-            }
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(ImsConnectionStateListener::onImsSuspended);
-            }
-        }
-
-        @Override
-        public void registrationServiceCapabilityChanged(int serviceClass, int event) {
-            log("registrationServiceCapabilityChanged :: serviceClass=" +
-                    serviceClass + ", event=" + event);
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onImsConnected(
-                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
-            }
-        }
-
-        @Override
-        public void registrationFeatureCapabilityChanged(int serviceClass,
-                int[] enabledFeatures, int[] disabledFeatures) {
-            log("registrationFeatureCapabilityChanged :: serviceClass=" +
-                    serviceClass);
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onFeatureCapabilityChanged(serviceClass,
-                        enabledFeatures, disabledFeatures));
-            }
-        }
-
-        @Override
-        public void voiceMessageCountUpdate(int count) {
-            log("voiceMessageCountUpdate :: count=" + count);
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onVoiceMessageCountChanged(count));
-            }
-        }
-
-        @Override
-        public void registrationAssociatedUriChanged(Uri[] uris) {
-            if (DBG) log("registrationAssociatedUriChanged ::");
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.registrationAssociatedUriChanged(uris));
-            }
-        }
-
-        @Override
-        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
-            if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
-                    ", imsReasonInfo=" + imsReasonInfo);
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onRegistrationChangeFailed(targetAccessTech,
-                        imsReasonInfo));
-            }
-        }
-    }
-
-    // New API for Registration, uses ImsConnectionStateListener for backwards compatibility with
-    // deprecated APIs.
-    private class ImsRegistrationCallback extends IImsRegistrationCallback.Stub {
-
-        @Override
-        public void onRegistered(int imsRadioTech) {
-            if (DBG) log("onRegistered ::");
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onRegistered(imsRadioTech));
-            }
-        }
-
-        @Override
-        public void onRegistering(int imsRadioTech) {
-            if (DBG) log("onRegistering ::");
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onRegistering(imsRadioTech));
-            }
-        }
-
-        @Override
-        public void onDeregistered(ImsReasonInfo imsReasonInfo) {
-            if (DBG) log("onDeregistered ::");
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onDeregistered(imsReasonInfo));
-            }
-        }
-
-        @Override
-        public void onTechnologyChangeFailed(int targetRadioTech, ImsReasonInfo imsReasonInfo) {
-            if (DBG) log("onTechnologyChangeFailed :: targetAccessTech=" + targetRadioTech +
-                    ", imsReasonInfo=" + imsReasonInfo);
-
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onTechnologyChangeFailed(targetRadioTech,
-                        imsReasonInfo));
-            }
-        }
-
-        @Override
-        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
-            if (DBG) log("onSubscriberAssociatedUriChanged");
-            synchronized (mRegistrationListeners) {
-                mRegistrationListeners.forEach(l -> l.onSubscriberAssociatedUriChanged(uris));
-            }
-        }
-    }
-
-    /**
      * Gets the ECBM interface to request ECBM exit.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
      * @return the ECBM interface instance
      * @throws ImsException if getting the ECBM interface results in an error
      */
-    public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
+    public ImsEcbm getEcbmInterface() throws ImsException {
         if (mEcbm != null && mEcbm.isBinderAlive()) {
             return mEcbm;
         }
 
         checkAndThrowExceptionIfServiceUnavailable();
         try {
-            IImsEcbm iEcbm = mImsServiceProxy.getEcbmInterface();
+            IImsEcbm iEcbm = mMmTelFeatureConnection.getEcbmInterface();
 
             if (iEcbm == null) {
                 throw new ImsException("getEcbmInterface()",
@@ -2406,7 +1984,7 @@
     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
             byte[] pdu) throws ImsException {
         try {
-            mImsServiceProxy.sendSms(token, messageRef, format, smsc, isRetry, pdu);
+            mMmTelFeatureConnection.sendSms(token, messageRef, format, smsc, isRetry, pdu);
         } catch (RemoteException e) {
             throw new ImsException("sendSms()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
@@ -2414,7 +1992,7 @@
 
     public void acknowledgeSms(int token, int messageRef, int result) throws ImsException {
         try {
-            mImsServiceProxy.acknowledgeSms(token, messageRef, result);
+            mMmTelFeatureConnection.acknowledgeSms(token, messageRef, result);
         } catch (RemoteException e) {
             throw new ImsException("acknowledgeSms()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -2423,7 +2001,7 @@
 
     public void acknowledgeSmsReport(int token, int messageRef, int result) throws  ImsException{
         try {
-            mImsServiceProxy.acknowledgeSmsReport(token, messageRef, result);
+            mMmTelFeatureConnection.acknowledgeSmsReport(token, messageRef, result);
         } catch (RemoteException e) {
             throw new ImsException("acknowledgeSmsReport()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -2432,7 +2010,7 @@
 
     public String getSmsFormat() throws ImsException{
         try {
-            return mImsServiceProxy.getSmsFormat();
+            return mMmTelFeatureConnection.getSmsFormat();
         } catch (RemoteException e) {
             throw new ImsException("getSmsFormat()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -2441,7 +2019,7 @@
 
     public void setSmsListener(IImsSmsListener listener) throws ImsException {
         try {
-            mImsServiceProxy.setSmsListener(listener);
+            mMmTelFeatureConnection.setSmsListener(listener);
         } catch (RemoteException e) {
             throw new ImsException("setSmsListener()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -2450,36 +2028,27 @@
 
     public void onSmsReady() throws ImsException{
         try {
-            mImsServiceProxy.onSmsReady();
+            mMmTelFeatureConnection.onSmsReady();
         } catch (RemoteException e) {
             throw new ImsException("onSmsReady()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
     }
 
-    public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback) {
-        // TODO: implement (coming in ag/3472519)
-    }
-
-    public void addCapabilitiesCallback(ImsFeature.CapabilityCallback callback) {
-        // TODO: implement (coming in ag/3472519)
-    }
-
     /**
      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
      * @return the multi-endpoint interface instance
      * @throws ImsException if getting the multi-endpoint interface results in an error
      */
-    public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
+    public ImsMultiEndpoint getMultiEndpointInterface() throws ImsException {
         if (mMultiEndpoint != null && mMultiEndpoint.isBinderAlive()) {
             return mMultiEndpoint;
         }
 
         checkAndThrowExceptionIfServiceUnavailable();
         try {
-            IImsMultiEndpoint iImsMultiEndpoint = mImsServiceProxy.getMultiEndpointInterface();
+            IImsMultiEndpoint iImsMultiEndpoint = mMmTelFeatureConnection.getMultiEndpointInterface();
 
             if (iImsMultiEndpoint == null) {
                 throw new ImsException("getMultiEndpointInterface()",
@@ -2518,7 +2087,9 @@
     public void factoryReset() {
         // Set VoLTE to default
         SubscriptionManager.setSubscriptionProperty(getSubId(),
-                SubscriptionManager.ENHANCED_4G_MODE_ENABLED, booleanToPropertyString(true));
+                SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
+                booleanToPropertyString(getBooleanCarrierConfig(
+                        CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL)));
 
         // Set VoWiFi to default
         SubscriptionManager.setSubscriptionProperty(getSubId(),
@@ -2547,40 +2118,22 @@
     }
 
     private boolean isDataEnabled() {
-        return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
-    }
-
-    /**
-     * Set data enabled/disabled flag.
-     * @param enabled True if data is enabled, otherwise disabled.
-     */
-    public void setDataEnabled(boolean enabled) {
-        log("setDataEnabled: " + enabled);
-        SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
+        return new TelephonyManager(mContext, getSubId()).isMobileDataEnabled();
     }
 
     private boolean isVolteProvisioned() {
-        return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
-    }
-
-    private void setVolteProvisionedProperty(boolean provisioned) {
-        SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
+        return getProvisionedBoolNoException(
+                ImsConfig.ConfigConstants.VLT_SETTING_ENABLED);
     }
 
     private boolean isWfcProvisioned() {
-        return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
-    }
-
-    private void setWfcProvisionedProperty(boolean provisioned) {
-        SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
+        return getProvisionedBoolNoException(
+                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED);
     }
 
     private boolean isVtProvisioned() {
-        return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
-    }
-
-    private void setVtProvisionedProperty(boolean provisioned) {
-        SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
+        return getProvisionedBoolNoException(
+                ImsConfig.ConfigConstants.LVC_SETTING_ENABLED);
     }
 
     private static String booleanToPropertyString(boolean bool) {
@@ -2592,7 +2145,7 @@
         pw.println("ImsManager:");
         pw.println("  mPhoneId = " + mPhoneId);
         pw.println("  mConfigUpdated = " + mConfigUpdated);
-        pw.println("  mImsServiceProxy = " + mImsServiceProxy);
+        pw.println("  mImsServiceProxy = " + mMmTelFeatureConnection);
         pw.println("  mDataEnabled = " + isDataEnabled());
         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(
                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
diff --git a/src/java/com/android/ims/ImsMultiEndpoint.java b/src/java/com/android/ims/ImsMultiEndpoint.java
index 9692696..1315dbd 100644
--- a/src/java/com/android/ims/ImsMultiEndpoint.java
+++ b/src/java/com/android/ims/ImsMultiEndpoint.java
@@ -21,6 +21,8 @@
 
 import android.os.RemoteException;
 import android.telephony.Rlog;
+import android.telephony.ims.ImsExternalCallState;
+import android.telephony.ims.ImsReasonInfo;
 
 import java.util.List;
 
diff --git a/src/java/com/android/ims/ImsServiceBase.java b/src/java/com/android/ims/ImsServiceBase.java
index 66122df..0a15c9d 100644
--- a/src/java/com/android/ims/ImsServiceBase.java
+++ b/src/java/com/android/ims/ImsServiceBase.java
@@ -18,7 +18,7 @@
 
 import android.app.PendingIntent;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsConfig;
diff --git a/src/java/com/android/ims/ImsServiceProxy.java b/src/java/com/android/ims/ImsServiceProxy.java
deleted file mode 100644
index a874436..0000000
--- a/src/java/com/android/ims/ImsServiceProxy.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.ims;
-
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.aidl.IImsCallSessionListener;
-import android.telephony.ims.feature.ImsFeature;
-import android.telephony.SmsMessage;
-import android.telephony.ims.internal.stub.SmsImplBase;
-import android.util.Log;
-
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceFeatureCallback;
-import com.android.ims.internal.IImsSmsListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel and RCS.
- * @hide
- */
-
-public class ImsServiceProxy {
-
-    protected static final String TAG = "ImsServiceProxy";
-    protected final int mSlotId;
-    protected IBinder mBinder;
-    private final int mSupportedFeature;
-    private Context mContext;
-
-    // Start by assuming the proxy is available for usage.
-    private boolean mIsAvailable = true;
-    // ImsFeature Status from the ImsService. Cached.
-    private Integer mFeatureStatusCached = null;
-    private IFeatureUpdate mStatusCallback;
-    private final Object mLock = new Object();
-
-    public static ImsServiceProxy create(Context context , int slotId) {
-        ImsServiceProxy serviceProxy = new ImsServiceProxy(context, slotId, ImsFeature.MMTEL);
-
-        TelephonyManager tm  = getTelephonyManager(context);
-        if (tm == null) {
-            Rlog.w(TAG, "getServiceProxy: TelephonyManager is null!");
-            // Binder can be unset in this case because it will be torn down/recreated as part of
-            // a retry mechanism until the serviceProxy binder is set successfully.
-            return serviceProxy;
-        }
-
-        IImsMMTelFeature binder = tm.getImsMMTelFeatureAndListen(slotId,
-                serviceProxy.getListener());
-        if (binder != null) {
-            serviceProxy.setBinder(binder.asBinder());
-            // Trigger the cache to be updated for feature status.
-            serviceProxy.getFeatureStatus();
-        } else {
-            Rlog.w(TAG, "getServiceProxy: binder is null! Phone Id: " + slotId);
-        }
-        return serviceProxy;
-    }
-
-    public static TelephonyManager getTelephonyManager(Context context) {
-        return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-    }
-
-    public interface IFeatureUpdate {
-        /**
-         * Called when the ImsFeature has changed its state. Use
-         * {@link ImsFeature#getFeatureState()} to get the new state.
-         */
-        void notifyStateChanged();
-
-        /**
-         * Called when the ImsFeature has become unavailable due to the binder switching or app
-         * crashing. A new ImsServiceProxy should be requested for that feature.
-         */
-        void notifyUnavailable();
-    }
-
-    private final IImsServiceFeatureCallback mListenerBinder =
-            new IImsServiceFeatureCallback.Stub() {
-
-        @Override
-        public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
-            // The feature has been re-enabled. This may happen when the service crashes.
-            synchronized (mLock) {
-                if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
-                    Log.i(TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
-                            feature);
-                    mIsAvailable = true;
-                }
-            }
-        }
-
-        @Override
-        public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
-            synchronized (mLock) {
-                if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
-                    Log.i(TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
-                            feature);
-                    mIsAvailable = false;
-                    if (mStatusCallback != null) {
-                        mStatusCallback.notifyUnavailable();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
-            synchronized (mLock) {
-                Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
-                        " status: " + status);
-                if (mSlotId == slotId && feature == mSupportedFeature) {
-                    mFeatureStatusCached = status;
-                    if (mStatusCallback != null) {
-                        mStatusCallback.notifyStateChanged();
-                    }
-                }
-            }
-        }
-    };
-
-    public ImsServiceProxy(Context context, int slotId, IBinder binder, int featureType) {
-        mSlotId = slotId;
-        mBinder = binder;
-        mSupportedFeature = featureType;
-        mContext = context;
-    }
-
-    public ImsServiceProxy(Context context, int slotId, int featureType) {
-        this(context, slotId, null, featureType);
-    }
-
-    public @Nullable IImsRegistration getRegistration() {
-        TelephonyManager tm = getTelephonyManager(mContext);
-        return tm != null ? tm.getImsRegistration(mSlotId, ImsFeature.MMTEL) : null;
-    }
-
-    public IImsServiceFeatureCallback getListener() {
-        return mListenerBinder;
-    }
-
-    public void setBinder(IBinder binder) {
-        mBinder = binder;
-    }
-
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).startSession(incomingCallIntent, listener);
-        }
-    }
-
-    public void endSession(int sessionId) throws RemoteException {
-        synchronized (mLock) {
-            // Only check to make sure the binder connection still exists. This method should
-            // still be able to be called when the state is STATE_NOT_AVAILABLE.
-            checkBinderConnection();
-            getServiceInterface(mBinder).endSession(sessionId);
-        }
-    }
-
-    public boolean isConnected(int callServiceType, int callType)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).isConnected(callServiceType, callType);
-        }
-    }
-
-    public boolean isOpened() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).isOpened();
-        }
-    }
-
-    public void addRegistrationListener(IImsRegistrationListener listener)
-    throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).addRegistrationListener(listener);
-        }
-    }
-
-    public void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).removeRegistrationListener(listener);
-        }
-    }
-
-    public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType,
-                    callType);
-        }
-    }
-
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).createCallSession(sessionId, profile);
-        }
-    }
-
-    public IImsCallSession getPendingCallSession(int sessionId, String callId)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
-        }
-    }
-
-    public IImsUt getUtInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getUtInterface();
-        }
-    }
-
-    public IImsConfig getConfigInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getConfigInterface();
-        }
-    }
-
-    public void turnOnIms() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).turnOnIms();
-        }
-    }
-
-    public void turnOffIms() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).turnOffIms();
-        }
-    }
-
-    public IImsEcbm getEcbmInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getEcbmInterface();
-        }
-    }
-
-    public void setUiTTYMode(int uiTtyMode, Message onComplete)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).setUiTTYMode(uiTtyMode, onComplete);
-        }
-    }
-
-    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getMultiEndpointInterface();
-        }
-    }
-
-    /**
-     * @return an integer describing the current Feature Status, defined in
-     * {@link ImsFeature.ImsState}.
-     */
-    public int getFeatureStatus() {
-        synchronized (mLock) {
-            if (isBinderAlive() && mFeatureStatusCached != null) {
-                Log.i(TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
-                return mFeatureStatusCached;
-            }
-        }
-        // Don't synchronize on Binder call.
-        Integer status = retrieveFeatureStatus();
-        synchronized (mLock) {
-            if (status == null) {
-                return ImsFeature.STATE_NOT_AVAILABLE;
-            }
-            // Cache only non-null value for feature status.
-            mFeatureStatusCached = status;
-        }
-        Log.i(TAG, "getFeatureStatus - returning " + status);
-        return status;
-    }
-
-    /**
-     * Internal method used to retrieve the feature status from the corresponding ImsService.
-     */
-    private Integer retrieveFeatureStatus() {
-        if (mBinder != null) {
-            try {
-                return getServiceInterface(mBinder).getFeatureStatus();
-            } catch (RemoteException e) {
-                // Status check failed, don't update cache
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @param c Callback that will fire when the feature status has changed.
-     */
-    public void setStatusCallback(IFeatureUpdate c) {
-        mStatusCallback = c;
-    }
-
-    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
-            byte[] pdu) throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
-                    pdu);
-        }
-    }
-
-    public void acknowledgeSms(int token, int messageRef,
-            @SmsImplBase.SendStatusResult int result) throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
-        }
-    }
-
-    public void acknowledgeSmsReport(int token, int messageRef,
-            @SmsImplBase.StatusReportResult int result) throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
-        }
-    }
-
-    public String getSmsFormat() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getSmsFormat();
-        }
-    }
-
-    public void onSmsReady() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).onSmsReady();
-        }
-    }
-
-    public void setSmsListener(IImsSmsListener listener) throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).setSmsListener(listener);
-        }
-    }
-
-    /**
-     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
-     * method returns false, it doesn't mean that the Binder connection is not available (use
-     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
-     * at this time.
-     *
-     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
-     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
-     */
-    public boolean isBinderReady() {
-        return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
-    }
-
-    /**
-     * @return false if the binder connection is no longer alive.
-     */
-    public boolean isBinderAlive() {
-        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
-    }
-
-    protected void checkServiceIsReady() throws RemoteException {
-        if (!isBinderReady()) {
-            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
-        }
-    }
-
-    private IImsMMTelFeature getServiceInterface(IBinder b) {
-        return IImsMMTelFeature.Stub.asInterface(b);
-    }
-
-    protected void checkBinderConnection() throws RemoteException {
-        if (!isBinderAlive()) {
-            throw new RemoteException("ImsServiceProxy is not available for that feature.");
-        }
-    }
-}
diff --git a/src/java/com/android/ims/ImsServiceProxyCompat.java b/src/java/com/android/ims/ImsServiceProxyCompat.java
deleted file mode 100644
index d5f211a..0000000
--- a/src/java/com/android/ims/ImsServiceProxyCompat.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.ims;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.feature.ImsFeature;
-
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility class that implements the new ImsService MMTelFeature interface, but
- * uses the old IImsService interface to support older devices that implement the deprecated
- * opt/net/ims interface.
- * @hide
- */
-
-public class ImsServiceProxyCompat extends ImsServiceProxy {
-
-    private static final int SERVICE_ID = ImsFeature.MMTEL;
-
-    /**
-     * For accessing the IMS related service.
-     * Internal use only.
-     * @hide
-     */
-    private static final String IMS_SERVICE = "ims";
-
-    public static ImsServiceProxyCompat create(Context context, int slotId,
-            IBinder.DeathRecipient recipient) {
-        IBinder binder = ServiceManager.checkService(IMS_SERVICE);
-
-        if (binder != null) {
-            try {
-                binder.linkToDeath(recipient, 0);
-            } catch (RemoteException e) {
-            }
-        }
-
-        // If the proxy is created with a null binder, subsequent calls that depend on a live
-        // binder will fail, causing this structure to be torn down and created again.
-        return new ImsServiceProxyCompat(context, slotId, binder);
-    }
-
-    public ImsServiceProxyCompat(Context context, int slotId, IBinder binder) {
-        super(context, slotId, binder, SERVICE_ID);
-    }
-
-    @Override
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
-                listener);
-    }
-
-    @Override
-    public void endSession(int sessionId) throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).close(sessionId);
-    }
-
-    @Override
-    public boolean isConnected(int callServiceType, int callType)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).isConnected(SERVICE_ID,  callServiceType, callType);
-    }
-
-    @Override
-    public boolean isOpened() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).isOpened(SERVICE_ID);
-    }
-
-    @Override
-    public void addRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
-    }
-
-    @Override
-    public void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        // Not Implemented in old ImsService. If the registration listener becomes invalid, the
-        // ImsService will remove.
-    }
-
-    @Override
-    public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
-    }
-
-    @Override
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).createCallSession(sessionId, profile, null);
-    }
-
-    @Override
-    public IImsCallSession getPendingCallSession(int sessionId, String callId)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
-    }
-
-    @Override
-    public IImsUt getUtInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
-    }
-
-    @Override
-    public IImsConfig getConfigInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getConfigInterface(mSlotId);
-    }
-
-    @Override
-    public void turnOnIms() throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).turnOnIms(mSlotId);
-    }
-
-    @Override
-    public void turnOffIms() throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).turnOffIms(mSlotId);
-    }
-
-    @Override
-    public IImsEcbm getEcbmInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
-    }
-
-    @Override
-    public void setUiTTYMode(int uiTtyMode, Message onComplete)
-            throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
-    }
-
-    @Override
-    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
-    }
-    @Override
-    public int getFeatureStatus() {
-        return ImsFeature.STATE_READY;
-    }
-
-    @Override
-    public boolean isBinderAlive() {
-        return mBinder != null && mBinder.isBinderAlive();
-    }
-
-    private IImsService getServiceInterface(IBinder b) {
-        return IImsService.Stub.asInterface(b);
-    }
-}
diff --git a/src/java/com/android/ims/ImsUt.java b/src/java/com/android/ims/ImsUt.java
index eaeb551..b3d4c8a 100644
--- a/src/java/com/android/ims/ImsUt.java
+++ b/src/java/com/android/ims/ImsUt.java
@@ -27,6 +27,10 @@
 import android.os.Registrant;
 import android.os.RemoteException;
 import android.telephony.Rlog;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java
new file mode 100644
index 0000000..571670d
--- /dev/null
+++ b/src/java/com/android/ims/MmTelFeatureConnection.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.ims;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.ims.internal.IImsUt;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
+ * the platform currently supports: MMTel and RCS.
+ * @hide
+ */
+
+public class MmTelFeatureConnection {
+
+    protected static final String TAG = "MmTelFeatureConnection";
+    protected final int mSlotId;
+    protected IBinder mBinder;
+    private Context mContext;
+
+    // Start by assuming the proxy is available for usage.
+    private volatile boolean mIsAvailable = true;
+    // ImsFeature Status from the ImsService. Cached.
+    private Integer mFeatureStateCached = null;
+    private IFeatureUpdate mStatusCallback;
+    private final Object mLock = new Object();
+
+    private MmTelFeature.Listener mMmTelFeatureListener;
+
+    private abstract class CallbackAdapterManager<T> {
+        private static final String TAG = "CallbackAdapterManager";
+
+        protected final Set<T> mLocalCallbacks = new HashSet<>();
+        private boolean mHasConnected = false;
+
+        public void addCallback(T localCallback) throws RemoteException {
+            // We only one one binding to the ImsService per process.
+            // Store any more locally.
+            synchronized (mLock) {
+                if(!mHasConnected) {
+                    // throws a RemoteException if a connection can not be established.
+                    if(createConnection()) {
+                        mHasConnected = true;
+                    } else {
+                        throw new RemoteException("Can not create connection!");
+                    }
+                }
+                Log.i(TAG, "Local callback added: " + localCallback);
+                mLocalCallbacks.add(localCallback);
+            }
+        }
+
+        public void removeCallback(T localCallback) {
+            // We only maintain one binding to the ImsService per process.
+            synchronized (mLock) {
+                Log.i(TAG, "Local callback removed: " + localCallback);
+                mLocalCallbacks.remove(localCallback);
+            // If we have removed all local callbacks, remove callback to ImsService.
+                if(mHasConnected) {
+                    if (mLocalCallbacks.isEmpty()) {
+                        removeConnection();
+                        mHasConnected = false;
+                    }
+                }
+            }
+        }
+
+        public void close() {
+            synchronized (mLock) {
+                if (mHasConnected) {
+                    removeConnection();
+                    // Still mark the connection as disconnected, even if this fails.
+                    mHasConnected = false;
+                }
+                Log.i(TAG, "Closing connection and clearing callbacks");
+                mLocalCallbacks.clear();
+            }
+        }
+
+        abstract boolean createConnection() throws RemoteException;
+
+        abstract void removeConnection();
+    }
+    private ImsRegistrationCallbackAdapter mRegistrationCallbackManager
+            = new ImsRegistrationCallbackAdapter();
+    private class ImsRegistrationCallbackAdapter
+            extends CallbackAdapterManager<ImsRegistrationImplBase.Callback> {
+        private final RegistrationCallbackAdapter mRegistrationCallbackAdapter
+                = new RegistrationCallbackAdapter();
+
+        private class RegistrationCallbackAdapter extends IImsRegistrationCallback.Stub {
+
+            @Override
+            public void onRegistered(int imsRadioTech) {
+                Log.i(TAG, "onRegistered ::");
+
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(l -> l.onRegistered(imsRadioTech));
+                }
+            }
+
+            @Override
+            public void onRegistering(int imsRadioTech) {
+                Log.i(TAG, "onRegistering ::");
+
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(l -> l.onRegistering(imsRadioTech));
+                }
+            }
+
+            @Override
+            public void onDeregistered(ImsReasonInfo imsReasonInfo) {
+                Log.i(TAG, "onDeregistered ::");
+
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(l -> l.onDeregistered(imsReasonInfo));
+                }
+            }
+
+            @Override
+            public void onTechnologyChangeFailed(int targetRadioTech, ImsReasonInfo imsReasonInfo) {
+                Log.i(TAG, "onTechnologyChangeFailed :: targetAccessTech=" + targetRadioTech +
+                        ", imsReasonInfo=" + imsReasonInfo);
+
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(l -> l.onTechnologyChangeFailed(targetRadioTech,
+                            imsReasonInfo));
+                }
+            }
+
+            @Override
+            public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+                Log.i(TAG, "onSubscriberAssociatedUriChanged");
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(l -> l.onSubscriberAssociatedUriChanged(uris));
+                }
+            }
+        }
+
+        @Override
+        boolean createConnection() throws RemoteException {
+            IImsRegistration imsRegistration = getRegistration();
+            if (imsRegistration != null) {
+                getRegistration().addRegistrationCallback(mRegistrationCallbackAdapter);
+                return true;
+            } else {
+                Log.e(TAG, "ImsRegistration is null");
+                return false;
+            }
+        }
+
+        @Override
+        void removeConnection() {
+            IImsRegistration imsRegistration = getRegistration();
+            if (imsRegistration != null) {
+                try {
+                    getRegistration().addRegistrationCallback(mRegistrationCallbackAdapter);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "removeConnection: couldn't remove registration callback");
+                }
+            } else {
+                Log.e(TAG, "ImsRegistration is null");
+            }
+        }
+    }
+
+    private final CapabilityCallbackManager mCapabilityCallbackManager
+            = new CapabilityCallbackManager();
+    private class CapabilityCallbackManager
+            extends CallbackAdapterManager<ImsFeature.CapabilityCallback> {
+        private final CapabilityCallbackAdapter mCallbackAdapter = new CapabilityCallbackAdapter();
+
+        private class CapabilityCallbackAdapter extends ImsFeature.CapabilityCallback {
+            // Called when the Capabilities Status on this connection have changed.
+            @Override
+            public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+                synchronized (mLock) {
+                    mLocalCallbacks.forEach(
+                            callback -> callback.onCapabilitiesStatusChanged(config));
+                }
+            }
+        }
+
+        @Override
+        boolean createConnection() throws RemoteException {
+            IImsMmTelFeature binder = getServiceInterface(mBinder);
+            if (binder != null) {
+                binder.addCapabilityCallback(mCallbackAdapter);
+                return true;
+            } else {
+                Log.w(TAG, "create: Couldn't get IImsMmTelFeature binder");
+                return false;
+            }
+        }
+
+        @Override
+        void removeConnection() {
+            IImsMmTelFeature binder = getServiceInterface(mBinder);
+            if (binder != null) {
+                try {
+                    binder.removeCapabilityCallback(mCallbackAdapter);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "remove: IImsMmTelFeature binder is dead");
+                }
+            } else {
+                Log.w(TAG, "remove: Couldn't get IImsMmTelFeature binder");
+            }
+        }
+    }
+
+
+    public static MmTelFeatureConnection create(Context context , int slotId) {
+        MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId);
+
+        TelephonyManager tm  = getTelephonyManager(context);
+        if (tm == null) {
+            Rlog.w(TAG, "create: TelephonyManager is null!");
+            // Binder can be unset in this case because it will be torn down/recreated as part of
+            // a retry mechanism until the serviceProxy binder is set successfully.
+            return serviceProxy;
+        }
+
+        IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId,
+                serviceProxy.getListener());
+        if (binder != null) {
+            serviceProxy.setBinder(binder.asBinder());
+            // Trigger the cache to be updated for feature status.
+            serviceProxy.getFeatureState();
+        } else {
+            Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
+        }
+        return serviceProxy;
+    }
+
+    public static TelephonyManager getTelephonyManager(Context context) {
+        return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+    }
+
+    public interface IFeatureUpdate {
+        /**
+         * Called when the ImsFeature has changed its state. Use
+         * {@link ImsFeature#getFeatureState()} to get the new state.
+         */
+        void notifyStateChanged();
+
+        /**
+         * Called when the ImsFeature has become unavailable due to the binder switching or app
+         * crashing. A new ImsServiceProxy should be requested for that feature.
+         */
+        void notifyUnavailable();
+    }
+
+    private final IImsServiceFeatureCallback mListenerBinder =
+            new IImsServiceFeatureCallback.Stub() {
+
+        @Override
+        public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
+            // The feature has been re-enabled. This may happen when the service crashes.
+            synchronized (mLock) {
+                if (!mIsAvailable && mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
+                    Log.i(TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
+                            feature);
+                    mIsAvailable = true;
+                }
+            }
+        }
+
+        @Override
+        public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
+            synchronized (mLock) {
+                if (mIsAvailable && mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
+                    Log.i(TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
+                            feature);
+                    mIsAvailable = false;
+                    if (mStatusCallback != null) {
+                        mStatusCallback.notifyUnavailable();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
+            synchronized (mLock) {
+                Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
+                        " status: " + status);
+                if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
+                    mFeatureStateCached = status;
+                    if (mStatusCallback != null) {
+                        mStatusCallback.notifyStateChanged();
+                    }
+                }
+            }
+        }
+    };
+
+    public MmTelFeatureConnection(Context context, int slotId) {
+        mSlotId = slotId;
+        mContext = context;
+    }
+
+    private @Nullable IImsRegistration getRegistration() {
+        TelephonyManager tm = getTelephonyManager(mContext);
+        return tm != null ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
+    }
+
+    private IImsConfig getConfig() {
+        TelephonyManager tm = getTelephonyManager(mContext);
+        return tm != null ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
+    }
+
+    public IImsServiceFeatureCallback getListener() {
+        return mListenerBinder;
+    }
+
+    public void setBinder(IBinder binder) {
+        mBinder = binder;
+    }
+
+    /**
+     * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
+     * framework. Calling this method multiple times will reset the listener attached to the
+     * {@link MmTelFeature}.
+     * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature}
+     * to notify the framework of updates.
+     */
+    public void openConnection(MmTelFeature.Listener listener) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            mMmTelFeatureListener = listener;
+            getServiceInterface(mBinder).setListener(mMmTelFeatureListener);
+        }
+    }
+
+    public void closeConnection() {
+        mRegistrationCallbackManager.close();
+        mCapabilityCallbackManager.close();
+        try {
+            getServiceInterface(mBinder).setListener(null);
+        } catch (RemoteException e) {
+            Log.w(TAG, "closeConnection: couldn't remove listener!");
+        }
+    }
+
+    public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+            throws RemoteException {
+        mRegistrationCallbackManager.addCallback(callback);
+    }
+
+    public void removeRegistrationCallback(ImsRegistrationImplBase.Callback callback)
+            throws RemoteException {
+        mRegistrationCallbackManager.removeCallback(callback);
+    }
+
+    public void addCapabilityCallback(ImsFeature.CapabilityCallback callback)
+            throws RemoteException {
+        mCapabilityCallbackManager.addCallback(callback);
+    }
+
+    public void removeCapabilityCallback(ImsFeature.CapabilityCallback callback)
+            throws RemoteException {
+        mCapabilityCallbackManager.removeCallback(callback);
+    }
+
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            ImsFeature.CapabilityCallback callback) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
+        }
+    }
+
+    public void queryEnabledCapabilities(int capability, int radioTech,
+            ImsFeature.CapabilityCallback callback) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech,
+                    callback);
+        }
+    }
+
+    public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return new MmTelFeature.MmTelCapabilities(
+                    getServiceInterface(mBinder).queryCapabilityStatus());
+        }
+    }
+
+    public ImsCallProfile createCallProfile(int callServiceType, int callType)
+            throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).createCallProfile(callServiceType, callType);
+        }
+    }
+
+    public IImsCallSession createCallSession(ImsCallProfile profile)
+            throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).createCallSession(profile);
+        }
+    }
+
+    public IImsUt getUtInterface() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).getUtInterface();
+        }
+    }
+
+    public IImsConfig getConfigInterface() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getConfig();
+        }
+    }
+
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
+    throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            IImsRegistration registration = getRegistration();
+            if (registration != null) {
+                return registration.getRegistrationTechnology();
+            } else {
+                return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+            }
+        }
+    }
+
+    public IImsEcbm getEcbmInterface() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).getEcbmInterface();
+        }
+    }
+
+    public void setUiTTYMode(int uiTtyMode, Message onComplete)
+            throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete);
+        }
+    }
+
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).getMultiEndpointInterface();
+        }
+    }
+
+    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry,
+                    pdu);
+        }
+    }
+
+    public void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.SendStatusResult int result) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result);
+        }
+    }
+
+    public void acknowledgeSmsReport(int token, int messageRef,
+            @ImsSmsImplBase.StatusReportResult int result) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result);
+        }
+    }
+
+    public String getSmsFormat() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            return getServiceInterface(mBinder).getSmsFormat();
+        }
+    }
+
+    public void onSmsReady() throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).onSmsReady();
+        }
+    }
+
+    public void setSmsListener(IImsSmsListener listener) throws RemoteException {
+        synchronized (mLock) {
+            checkServiceIsReady();
+            getServiceInterface(mBinder).setSmsListener(listener);
+        }
+    }
+
+    /**
+     * @return an integer describing the current Feature Status, defined in
+     * {@link ImsFeature.ImsState}.
+     */
+    public int getFeatureState() {
+        synchronized (mLock) {
+            if (isBinderAlive() && mFeatureStateCached != null) {
+                Log.i(TAG, "getFeatureState - returning cached: " + mFeatureStateCached);
+                return mFeatureStateCached;
+            }
+        }
+        // Don't synchronize on Binder call.
+        Integer status = retrieveFeatureState();
+        synchronized (mLock) {
+            if (status == null) {
+                return ImsFeature.STATE_UNAVAILABLE;
+            }
+            // Cache only non-null value for feature status.
+            mFeatureStateCached = status;
+        }
+        Log.i(TAG, "getFeatureState - returning " + status);
+        return status;
+    }
+
+    /**
+     * Internal method used to retrieve the feature status from the corresponding ImsService.
+     */
+    private Integer retrieveFeatureState() {
+        if (mBinder != null) {
+            try {
+                return getServiceInterface(mBinder).getFeatureState();
+            } catch (RemoteException e) {
+                // Status check failed, don't update cache
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @param c Callback that will fire when the feature status has changed.
+     */
+    public void setStatusCallback(IFeatureUpdate c) {
+        mStatusCallback = c;
+    }
+
+    /**
+     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
+     * method returns false, it doesn't mean that the Binder connection is not available (use
+     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
+     * at this time.
+     *
+     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
+     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
+     */
+    public boolean isBinderReady() {
+        return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
+    }
+
+    /**
+     * @return false if the binder connection is no longer alive.
+     */
+    public boolean isBinderAlive() {
+        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
+    }
+
+    protected void checkServiceIsReady() throws RemoteException {
+        if (!isBinderReady()) {
+            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
+        }
+    }
+
+    private IImsMmTelFeature getServiceInterface(IBinder b) {
+        return IImsMmTelFeature.Stub.asInterface(b);
+    }
+
+    protected void checkBinderConnection() throws RemoteException {
+        if (!isBinderAlive()) {
+            throw new RemoteException("ImsServiceProxy is not available for that feature.");
+        }
+    }
+}
diff --git a/src/java/com/android/ims/internal/VideoPauseTracker.java b/src/java/com/android/ims/internal/VideoPauseTracker.java
index baa3163..9243008 100644
--- a/src/java/com/android/ims/internal/VideoPauseTracker.java
+++ b/src/java/com/android/ims/internal/VideoPauseTracker.java
@@ -18,11 +18,11 @@
 
 import android.telecom.Log;
 import android.telecom.VideoProfile;
+import android.telephony.ims.ImsVideoCallProvider;
 import android.util.ArraySet;
 
 import java.util.Collection;
 import java.util.Set;
-import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 /**
@@ -71,7 +71,7 @@
      *
      * @param source The source of the pause request.
      * @return {@code true} if a pause should be issued to the
-     *      {@link com.android.ims.internal.ImsVideoCallProvider}, {@code false} otherwise.
+     *      {@link ImsVideoCallProvider}, {@code false} otherwise.
      */
     public boolean shouldPauseVideoFor(int source) {
         synchronized (mPauseRequestsLock) {
@@ -102,7 +102,7 @@
      *
      * @param source The source of the resume request.
      * @return {@code true} if a resume should be issued to the
-     *      {@link com.android.ims.internal.ImsVideoCallProvider}, {@code false} otherwise.
+     *      {@link ImsVideoCallProvider}, {@code false} otherwise.
      */
     public boolean shouldResumeVideoFor(int source) {
         synchronized (mPauseRequestsLock) {
diff --git a/tests/src/com/android/ims/ImsConfigTest.java b/tests/src/com/android/ims/ImsConfigTest.java
index 18d53b1..4cf7a92 100644
--- a/tests/src/com/android/ims/ImsConfigTest.java
+++ b/tests/src/com/android/ims/ImsConfigTest.java
@@ -17,8 +17,7 @@
 package com.android.ims;
 
 import android.support.test.runner.AndroidJUnit4;
-
-import com.android.ims.internal.IImsConfig;
+import android.telephony.ims.aidl.IImsConfig;
 
 import org.junit.After;
 import org.junit.Before;
@@ -59,6 +58,6 @@
 
         mTestImsConfig.getProvisionedValue(testItem);
 
-        verify(mMockImsConfigInterface).getProvisionedValue(eq(testItem));
+        verify(mMockImsConfigInterface).getConfigInt(eq(testItem));
     }
 }