Modifies ImsManager to support ImsResolver

This change modifies the ImsManager to support both the old and
new versions of ImsService. This is done by creating the
ImsServiceProxyCompat class, which implements IMMTelFeature
and uses the old IImsService interface. ImsServiceProxy then
extends ImsServiceProxyCompat, which implements the new interface
for newer devices, which use IImsServiceController.

A callback interface has also been introduced to the ImsManager, which
takes information back from the ImsService regarding its status
(NOT_READY,INITIALIZING,READY) and uses it to ensure that we are not
operating on an ImsService that is not READY.

Test: Manual
Merged-In: I46f1f7237dffcdd12b66c16cd319818d1d21c101
Change-Id: I46f1f7237dffcdd12b66c16cd319818d1d21c101
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
index 68d0dcc..3562b90 100644
--- a/src/java/com/android/ims/ImsManager.java
+++ b/src/java/com/android/ims/ImsManager.java
@@ -34,21 +34,29 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsServiceProxy;
+import android.telephony.ims.ImsServiceProxyCompat;
+import android.telephony.ims.feature.ImsFeature;
 
 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.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsServiceController;
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
 
 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.Optional;
+import java.util.Set;
 
 /**
  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
@@ -94,6 +102,7 @@
     /**
      * Action to broadcast when ImsService is up.
      * Internal use only.
+     * @deprecated
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_UP =
@@ -102,6 +111,7 @@
     /**
      * Action to broadcast when ImsService is down.
      * Internal use only.
+     * @deprecated
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_DOWN =
@@ -168,7 +178,8 @@
 
     private Context mContext;
     private int mPhoneId;
-    private IImsService mImsService = null;
+    private final boolean mConfigDynamicBind;
+    private ImsServiceProxyCompat mImsServiceProxy = null;
     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
     // Ut interface for the supplementary service configuration
     private ImsUt mUt = null;
@@ -183,6 +194,12 @@
 
     private ImsMultiEndpoint mMultiEndpoint = null;
 
+    private Set<ImsServiceProxy.INotifyStatusChanged> mStatusCallbacks = new HashSet<>();
+
+    // Keep track of the ImsRegistrationListenerProxys that have been created so that we can
+    // remove them from the ImsService.
+    private Set<ImsRegistrationListenerProxy> mRegistrationListeners = new HashSet<>();
+
     // 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";
@@ -859,32 +876,70 @@
         return isFeatureOn;
     }
 
-    private ImsManager(Context context, int phoneId) {
+    /**
+     * Do NOT use this directly, instead use {@link #getInstance}.
+     */
+    @VisibleForTesting
+    public ImsManager(Context context, int phoneId) {
         mContext = context;
         mPhoneId = phoneId;
-        createImsService(true);
+        mConfigDynamicBind = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dynamic_bind_ims);
+        addNotifyStatusChangedCallback(this::sendImsServiceIntent);
+        createImsService();
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent() {
+        int status = mImsServiceProxy.getFeatureStatus();
+        Intent intent;
+        switch (status) {
+            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
+        mContext.sendBroadcast(new Intent(intent));
+    }
+
+
+    /**
+     * @return Whether or not ImsManager is configured to Dynamically bind or not to support legacy
+     * devices.
+     */
+    public boolean isDynamicBinding() {
+        return mConfigDynamicBind;
     }
 
     /*
      * Returns a flag indicating whether the IMS service is available.
      */
     public boolean isServiceAvailable() {
-        if (mImsService != null) {
-            return true;
+        if (mImsServiceProxy == null) {
+            createImsService();
         }
-
-        IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
-        if (binder != null) {
-            return true;
-        }
-
-        return false;
+        // mImsServiceProxy will always create an ImsServiceProxy.
+        return mImsServiceProxy.isBinderAlive();
     }
 
     public void setImsConfigListener(ImsConfigListener listener) {
         mImsConfigListener = listener;
     }
 
+    public void addNotifyStatusChangedCallback(ImsServiceProxy.INotifyStatusChanged c) {
+        if (c != null) {
+            mStatusCallbacks.add(c);
+        }
+    }
+
     /**
      * Opens the IMS service for making calls and/or receiving generic IMS calls.
      * The caller may make subsquent calls through {@link #makeCall}.
@@ -906,7 +961,7 @@
      *      or {@code listener} is null
      * @throws ImsException if calling the IMS service results in an error
      * @see #getCallId
-     * @see #getServiceId
+     * @see #getImsSessionId
      */
     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
             ImsConnectionStateListener listener) throws ImsException {
@@ -923,7 +978,7 @@
         int result = 0;
 
         try {
-            result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
+            result = mImsServiceProxy.startSession(incomingCallPendingIntent,
                     createRegistrationListenerProxy(serviceClass, listener));
         } catch (RemoteException e) {
             throw new ImsException("open()", e,
@@ -949,6 +1004,37 @@
      * @throws NullPointerException if {@code listener} is null
      * @throws ImsException if calling the IMS service results in an error
      */
+    public void addRegistrationListener(int sessionId, int serviceClass,
+            ImsConnectionStateListener listener)
+            throws ImsException {
+        checkAndThrowExceptionIfServiceUnavailable();
+
+        if (listener == null) {
+            throw new NullPointerException("listener can't be null");
+        }
+
+        try {
+            ImsRegistrationListenerProxy p = createRegistrationListenerProxy(serviceClass,
+                    listener);
+            mRegistrationListeners.add(p);
+            mImsServiceProxy.addRegistrationListener(sessionId, p);
+        } catch (RemoteException e) {
+            throw new ImsException("addRegistrationListener()", e,
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+        }
+    }
+
+    /**
+     * Adds registration listener to the IMS service.
+     *
+     * @param serviceClass a service class specified in {@link ImsServiceClass}
+     *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
+     * @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 #addRegistrationListener(int, int, ImsConnectionStateListener)}
+     * instead.
+     */
     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
             throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
@@ -958,8 +1044,11 @@
         }
 
         try {
-            mImsService.addRegistrationListener(mPhoneId, serviceClass,
-                    createRegistrationListenerProxy(serviceClass, listener));
+            ImsRegistrationListenerProxy p = createRegistrationListenerProxy(serviceClass,
+                    listener);
+            // Only add the listener if it doesn't already exist in the set.
+            mRegistrationListeners.add(p);
+            mImsServiceProxy.addRegistrationListener(ImsFeature.MMTEL, p);
         } catch (RemoteException e) {
             throw new ImsException("addRegistrationListener()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -967,17 +1056,48 @@
     }
 
     /**
+     * Removes the registration listener from the IMS service.
+     *
+     * @param sessionId The session ID returned by open.
+     * @param listener Previously registered listener that will be removed. Can not be null.
+     * @throws NullPointerException if {@code listener} is null
+     * @throws ImsException if calling the IMS service results in an error
+     * instead.
+     */
+    public void removeRegistrationListener(int sessionId, ImsConnectionStateListener listener)
+            throws ImsException {
+        checkAndThrowExceptionIfServiceUnavailable();
+
+        if (listener == null) {
+            throw new NullPointerException("listener can't be null");
+        }
+
+        try {
+            Optional<ImsRegistrationListenerProxy> optionalProxy = mRegistrationListeners.stream()
+                    .filter(l -> listener.equals(l.mListener)).findFirst();
+            if(optionalProxy.isPresent()) {
+                ImsRegistrationListenerProxy p = optionalProxy.get();
+                mRegistrationListeners.remove(p);
+                mImsServiceProxy.removeRegistrationListener(sessionId, p);
+            }
+        } catch (RemoteException e) {
+            throw new ImsException("removeRegistrationListener()", e,
+                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+        }
+    }
+
+    /**
      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
      * All the resources that were allocated to the service are also released.
      *
-     * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
+     * @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 serviceId) throws ImsException {
+    public void close(int sessionId) throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            mImsService.close(serviceId);
+            mImsServiceProxy.endSession(sessionId);
         } catch (RemoteException e) {
             throw new ImsException("close()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -992,18 +1112,18 @@
     /**
      * Gets the configuration interface to provision / withdraw the supplementary service settings.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @param sessionId a session id which is obtained from {@link ImsManager#open}
      * @return the Ut interface instance
      * @throws ImsException if getting the Ut interface results in an error
      */
-    public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
+    public ImsUtInterface getSupplementaryServiceConfiguration(int sessionId)
             throws ImsException {
-        // FIXME: manage the multiple Ut interfaces based on the service id
-        if (mUt == null) {
+        // FIXME: manage the multiple Ut interfaces based on the session id
+        if (mUt == null || !mImsServiceProxy.isBinderAlive()) {
             checkAndThrowExceptionIfServiceUnavailable();
 
             try {
-                IImsUt iUt = mImsService.getUtInterface(serviceId);
+                IImsUt iUt = mImsServiceProxy.getUtInterface(sessionId);
 
                 if (iUt == null) {
                     throw new ImsException("getSupplementaryServiceConfiguration()",
@@ -1024,7 +1144,7 @@
      * Checks if the IMS service has successfully registered to the IMS network
      * with the specified service & call type.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @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_NORMAL}
      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
@@ -1037,12 +1157,12 @@
      *        false otherwise
      * @throws ImsException if calling the IMS service results in an error
      */
-    public boolean isConnected(int serviceId, int serviceType, int callType)
+    public boolean isConnected(int sessionId, int serviceType, int callType)
             throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            return mImsService.isConnected(serviceId, serviceType, callType);
+            return mImsServiceProxy.isConnected(sessionId, serviceType, callType);
         } catch (RemoteException e) {
             throw new ImsException("isServiceConnected()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1052,15 +1172,15 @@
     /**
      * Checks if the specified IMS service is opend.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @param sessionId a session id which is obtained from {@link ImsManager#open}
      * @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(int serviceId) throws ImsException {
+    public boolean isOpened(int sessionId) throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            return mImsService.isOpened(serviceId);
+            return mImsServiceProxy.isOpened(sessionId);
         } catch (RemoteException e) {
             throw new ImsException("isOpened()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1070,7 +1190,7 @@
     /**
      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @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}
@@ -1087,12 +1207,12 @@
      * @return a {@link ImsCallProfile} object
      * @throws ImsException if calling the IMS service results in an error
      */
-    public ImsCallProfile createCallProfile(int serviceId,
+    public ImsCallProfile createCallProfile(int sessionId,
             int serviceType, int callType) throws ImsException {
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            return mImsService.createCallProfile(serviceId, serviceType, callType);
+            return mImsServiceProxy.createCallProfile(sessionId, serviceType, callType);
         } catch (RemoteException e) {
             throw new ImsException("createCallProfile()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1102,7 +1222,7 @@
     /**
      * Creates a {@link ImsCall} to make a call.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @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
@@ -1110,10 +1230,10 @@
      * @return a {@link ImsCall} object
      * @throws ImsException if calling the IMS service results in an error
      */
-    public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
+    public ImsCall makeCall(int sessionId, ImsCallProfile profile, String[] callees,
             ImsCall.Listener listener) throws ImsException {
         if (DBG) {
-            log("makeCall :: serviceId=" + serviceId
+            log("makeCall :: sessionId=" + sessionId
                     + ", profile=" + profile);
         }
 
@@ -1122,7 +1242,7 @@
         ImsCall call = new ImsCall(mContext, profile);
 
         call.setListener(listener);
-        ImsCallSession session = createCallSession(serviceId, profile);
+        ImsCallSession session = createCallSession(sessionId, profile);
 
         if ((callees != null) && (callees.length == 1)) {
             call.start(session, callees[0]);
@@ -1136,16 +1256,16 @@
     /**
      * Creates a {@link ImsCall} to take an incoming call.
      *
-     * @param serviceId a service id which is obtained from {@link ImsManager#open}
+     * @param sessionId a session id which is obtained from {@link ImsManager#open}
      * @param incomingCallIntent 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 serviceId, Intent incomingCallIntent,
+    public ImsCall takeCall(int sessionId, Intent incomingCallIntent,
             ImsCall.Listener listener) throws ImsException {
         if (DBG) {
-            log("takeCall :: serviceId=" + serviceId
+            log("takeCall :: sessionId=" + sessionId
                     + ", incomingCall=" + incomingCallIntent);
         }
 
@@ -1156,9 +1276,9 @@
                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
         }
 
-        int incomingServiceId = getServiceId(incomingCallIntent);
+        int incomingServiceId = getImsSessionId(incomingCallIntent);
 
-        if (serviceId != incomingServiceId) {
+        if (sessionId != incomingServiceId) {
             throw new ImsException("Service id is mismatched in the incoming call intent",
                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
         }
@@ -1171,7 +1291,7 @@
         }
 
         try {
-            IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
+            IImsCallSession session = mImsServiceProxy.getPendingCallSession(sessionId, callId);
 
             if (session == null) {
                 throw new ImsException("No pending session for the call",
@@ -1197,11 +1317,11 @@
      */
     public ImsConfig getConfigInterface() throws ImsException {
 
-        if (mConfig == null) {
+        if (mConfig == null || !mImsServiceProxy.isBinderAlive()) {
             checkAndThrowExceptionIfServiceUnavailable();
 
             try {
-                IImsConfig config = mImsService.getConfigInterface(mPhoneId);
+                IImsConfig config = mImsServiceProxy.getConfigInterface(mPhoneId);
                 if (config == null) {
                     throw new ImsException("getConfigInterface()",
                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
@@ -1216,13 +1336,13 @@
         return mConfig;
     }
 
-    public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
+    public void setUiTTYMode(Context context, int sessionId, int uiTtyMode, Message onComplete)
             throws ImsException {
 
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
+            mImsServiceProxy.setUiTTYMode(sessionId, uiTtyMode, onComplete);
         } catch (RemoteException e) {
             throw new ImsException("setTTYMode()", e,
                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
@@ -1259,6 +1379,12 @@
         }
         return disconnectReasons;
     }
+    
+    public int getImsServiceStatus() throws ImsException {
+        checkAndThrowExceptionIfServiceUnavailable();
+
+        return mImsServiceProxy.getFeatureStatus();
+    }
 
     /**
      * Get the boolean config from carrier config manager.
@@ -1322,9 +1448,9 @@
      * Gets the service type from the specified incoming call broadcast intent.
      *
      * @param incomingCallIntent the incoming call broadcast intent
-     * @return the service identifier or -1 if the intent does not contain it
+     * @return the session identifier or -1 if the intent does not contain it
      */
-    private static int getServiceId(Intent incomingCallIntent) {
+    private static int getImsSessionId(Intent incomingCallIntent) {
         if (incomingCallIntent == null) {
             return (-1);
         }
@@ -1337,43 +1463,74 @@
      */
     private void checkAndThrowExceptionIfServiceUnavailable()
             throws ImsException {
-        if (mImsService == null) {
-            createImsService(true);
+        if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
+            createImsService();
 
-            if (mImsService == null) {
+            if (mImsServiceProxy == null) {
                 throw new ImsException("Service is unavailable",
                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
             }
         }
     }
 
-    private static String getImsServiceName(int phoneId) {
-        // TODO: MSIM implementation needs to decide on service name as a function of phoneId
-        return IMS_SERVICE;
+    /**
+     * Binds the IMS service to make/receive the call. Supports two methods of exposing an
+     * ImsService:
+     * 1) com.android.ims.ImsService implementation in ServiceManager (deprecated).
+     * 2) android.telephony.ims.ImsService implementation through ImsResolver.
+     */
+    private void createImsService() {
+        if (!mConfigDynamicBind) {
+            // Old method of binding
+            Rlog.i(TAG, "Creating ImsService using ServiceManager");
+            mImsServiceProxy = getServiceProxyCompat();
+        } else {
+            Rlog.i(TAG, "Creating ImsService using ImsResolver");
+            mImsServiceProxy = getServiceProxy();
+        }
     }
 
-    /**
-     * Binds the IMS service to make/receive the call.
-     */
-    private void createImsService(boolean checkService) {
-        if (checkService) {
-            IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
+    // Deprecated method of binding with the ImsService defined in the ServiceManager.
+    private ImsServiceProxyCompat getServiceProxyCompat() {
+        IBinder binder = ServiceManager.checkService(IMS_SERVICE);
 
-            if (binder == null) {
-                return;
-            }
-        }
-
-        IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
-
-        if (b != null) {
+        if (binder != null) {
             try {
-                b.linkToDeath(mDeathRecipient, 0);
+                binder.linkToDeath(mDeathRecipient, 0);
             } catch (RemoteException e) {
             }
         }
 
-        mImsService = IImsService.Stub.asInterface(b);
+        return new ImsServiceProxyCompat(mPhoneId, binder);
+    }
+
+    // New method of binding with the ImsResolver
+    private ImsServiceProxy getServiceProxy() {
+        TelephonyManager tm = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        ImsServiceProxy serviceProxy = new ImsServiceProxy(mPhoneId, ImsFeature.MMTEL);
+        // Returns null if the service is not available.
+        IImsServiceController b = tm.getImsServiceControllerAndListen(mPhoneId,
+                ImsFeature.MMTEL, serviceProxy.getListener());
+        if (b != null) {
+            serviceProxy.setBinder(b.asBinder());
+            serviceProxy.setStatusCallback(() -> mStatusCallbacks.forEach(
+                            ImsServiceProxy.INotifyStatusChanged::notifyStatusChanged));
+            // Trigger the cache to be updated for feature status.
+            serviceProxy.getFeatureStatus();
+            // In order to keep backwards compatibility with other services such as RcsService,
+            // we must broadcast the IMS_SERVICE_UP intent here. If it is not ready, IMS_SERVICE_UP
+            // will be called in this::sendImsServiceIntent. IMS_SERVICE_UP is sent by ImsService
+            // in the old ImsService implementation.
+            if (serviceProxy.isBinderAlive()) {
+                Intent intent = new Intent(ACTION_IMS_SERVICE_UP);
+                intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
+                mContext.sendBroadcast(new Intent(intent));
+            }
+        } else {
+            Rlog.w(TAG, "getServiceProxy: b is null! Phone Id: " + mPhoneId);
+        }
+        return serviceProxy;
     }
 
     /**
@@ -1387,7 +1544,7 @@
     private ImsCallSession createCallSession(int serviceId,
             ImsCallProfile profile) throws ImsException {
         try {
-            return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
+            return new ImsCallSession(mImsServiceProxy.createCallSession(serviceId, profile, null));
         } catch (RemoteException e) {
             return null;
         }
@@ -1419,7 +1576,7 @@
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            mImsService.turnOnIms(mPhoneId);
+            mImsServiceProxy.turnOnIms(mPhoneId);
         } catch (RemoteException e) {
             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
@@ -1480,7 +1637,7 @@
         checkAndThrowExceptionIfServiceUnavailable();
 
         try {
-            mImsService.turnOffIms(mPhoneId);
+            mImsServiceProxy.turnOffIms(mPhoneId);
         } catch (RemoteException e) {
             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
         }
@@ -1500,13 +1657,13 @@
     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
-            mImsService = null;
+            mImsServiceProxy = null;
             mUt = null;
             mConfig = null;
             mEcbm = null;
             mMultiEndpoint = null;
 
-            if (mContext != null) {
+            if (mContext != null && !isDynamicBinding()) {
                 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
                 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
                 mContext.sendBroadcast(new Intent(intent));
@@ -1662,11 +1819,11 @@
      * @throws ImsException if getting the ECBM interface results in an error
      */
     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
-        if (mEcbm == null) {
+        if (mEcbm == null || !mImsServiceProxy.isBinderAlive()) {
             checkAndThrowExceptionIfServiceUnavailable();
 
             try {
-                IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
+                IImsEcbm iEcbm = mImsServiceProxy.getEcbmInterface(serviceId);
 
                 if (iEcbm == null) {
                     throw new ImsException("getEcbmInterface()",
@@ -1689,11 +1846,11 @@
      * @throws ImsException if getting the multi-endpoint interface results in an error
      */
     public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
-        if (mMultiEndpoint == null) {
+        if (mMultiEndpoint == null || !mImsServiceProxy.isBinderAlive()) {
             checkAndThrowExceptionIfServiceUnavailable();
 
             try {
-                IImsMultiEndpoint iImsMultiEndpoint = mImsService.getMultiEndpointInterface(
+                IImsMultiEndpoint iImsMultiEndpoint = mImsServiceProxy.getMultiEndpointInterface(
                         serviceId);
 
                 if (iImsMultiEndpoint == null) {
@@ -1791,7 +1948,7 @@
         pw.println("ImsManager:");
         pw.println("  mPhoneId = " + mPhoneId);
         pw.println("  mConfigUpdated = " + mConfigUpdated);
-        pw.println("  mImsService = " + mImsService);
+        pw.println("  mImsServiceProxy = " + mImsServiceProxy);
         pw.println("  mDataEnabled = " + isDataEnabled());
 
         pw.println("  isGbaValid = " + isGbaValid(mContext));