Refactor ImsService to AIDL

The ImsService was originally designed as a flat
interface, all ImsFeatures would be forwarded
through ImsService. This has been refactored to
using AIDL interfaces for each feature, as it
is more straightforward.

Test: Manual, Telephony IMS Unit Tests
Change-Id: Ibe065ddec6f180eabda03cf06f842c642a11114f
diff --git a/Android.mk b/Android.mk
index dd53793..6fce011 100644
--- a/Android.mk
+++ b/Android.mk
@@ -519,10 +519,12 @@
 	telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \
         telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
         telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl \
+        telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl \
         telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \
+	telephony/java/com/android/ims/internal/IImsRcsFeature.aidl \
 	telephony/java/com/android/ims/internal/IImsService.aidl \
 	telephony/java/com/android/ims/internal/IImsServiceController.aidl \
-	telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl \
+	telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl \
 	telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
 	telephony/java/com/android/ims/internal/IImsUt.aidl \
 	telephony/java/com/android/ims/internal/IImsUtListener.aidl \
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index 17156e8..384540a 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -5194,7 +5194,7 @@
 com.android.ims.internal.IImsService
 com.android.ims.internal.IImsService$Stub
 com.android.ims.internal.IImsServiceController
-com.android.ims.internal.IImsServiceFeatureListener
+com.android.ims.internal.IImsServiceFeatureCallback
 com.android.ims.internal.IImsUt
 com.android.ims.internal.IImsUt$Stub
 com.android.ims.internal.IImsUtListener
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f461910..7ddd8b5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -52,8 +52,9 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.util.Log;
 
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.CellNetworkScanResult;
@@ -4584,27 +4585,78 @@
     public @interface Feature {}
 
     /**
-     * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS
-     * feature or {@link null} if the service is not available. If an ImsServiceController is
-     * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for
-     * feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsServiceController} for.
-     * @param feature The IMS Feature we are requesting, corresponding to {@link ImsFeature}.
+     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
+     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
+     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
      * @param callback Listener that will send updates to ImsManager when there are updates to
      * ImsServiceController.
-     * @return {@link IImsServiceController} interface for the feature specified or {@link null} if
+     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
      * it is unavailable.
      * @hide
      */
-    public IImsServiceController getImsServiceControllerAndListen(int slotIndex, @Feature int feature,
-            IImsServiceFeatureListener callback) {
+    public @Nullable IImsMMTelFeature getImsMMTelFeatureAndListen(int slotIndex,
+            IImsServiceFeatureCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getImsServiceControllerAndListen(slotIndex, feature, callback);
+                return telephony.getMMTelFeatureAndListen(slotIndex, callback);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsServiceControllerAndListen, RemoteException: " + e.getMessage());
+            Rlog.e(TAG, "getImsMMTelFeatureAndListen, RemoteException: "
+                    + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
+     * feature for emergency calling or {@link null} if the service is not available. If an
+     * MMTelFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
+     * listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * @param callback Listener that will send updates to ImsManager when there are updates to
+     * ImsServiceController.
+     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
+     * it is unavailable.
+     * @hide
+     */
+    public @Nullable IImsMMTelFeature getImsEmergencyMMTelFeatureAndListen(int slotIndex,
+            IImsServiceFeatureCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getEmergencyMMTelFeatureAndListen(slotIndex, callback);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsEmergencyMMTelFeatureAndListen, RemoteException: "
+                    + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS
+     * feature for emergency calling or {@link null} if the service is not available. If an
+     * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
+     * listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for.
+     * @param callback Listener that will send updates to ImsManager when there are updates to
+     * ImsServiceController.
+     * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if
+     * it is unavailable.
+     * @hide
+     */
+    public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex,
+            IImsServiceFeatureCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getRcsFeatureAndListen(slotIndex, callback);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: "
+                    + e.getMessage());
         }
         return null;
     }
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 9d91cc3..8230eaf 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,13 +16,11 @@
 
 package android.telephony.ims;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ims.feature.ImsFeature;
@@ -31,22 +29,13 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.ims.ImsCallProfile;
-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.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
 import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
-import com.android.ims.internal.IImsUt;
 import com.android.internal.annotations.VisibleForTesting;
 
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
-import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 
 /**
  * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
@@ -92,247 +81,38 @@
      */
     public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
 
-    // A map of slot Id -> Set of features corresponding to that slot.
-    private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>();
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
     /**
      * @hide
      */
-    // Implements all supported features as a flat interface.
     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
 
         @Override
-        public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+                IImsFeatureStatusCallback c) {
+            return createEmergencyMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createRcsFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
                 throws RemoteException {
-            synchronized (mFeatures) {
-                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature");
-                onCreateImsFeatureInternal(slotId, feature, c);
-            }
+            ImsService.this.removeImsFeature(slotId, featureType, c);
         }
-
-        @Override
-        public void removeImsFeature(int slotId, int feature,  IImsFeatureStatusCallback c)
-                throws RemoteException {
-            synchronized (mFeatures) {
-                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature");
-                onRemoveImsFeatureInternal(slotId, feature, c);
-            }
-        }
-
-        @Override
-        public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
-                IImsRegistrationListener listener) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.startSession(incomingCallIntent, listener);
-                }
-            }
-            return 0;
-        }
-
-        @Override
-        public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
-            synchronized (mFeatures) {
-                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession");
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.endSession(sessionId);
-                }
-            }
-        }
-
-        @Override
-        public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
-                throws RemoteException {
-            enforceReadPhoneStatePermission("isConnected");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.isConnected(callSessionType, callType);
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public boolean isOpened(int slotId, int featureType) throws RemoteException {
-            enforceReadPhoneStatePermission("isOpened");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.isOpened();
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
-            enforceReadPhoneStatePermission("getFeatureStatus");
-            int status = ImsFeature.STATE_NOT_AVAILABLE;
-            synchronized (mFeatures) {
-                SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
-                if (featureMap != null) {
-                    ImsFeature feature = getImsFeatureFromType(featureMap, featureType);
-                    if (feature != null) {
-                        status = feature.getFeatureState();
-                    }
-                }
-            }
-            return status;
-        }
-
-        @Override
-        public void addRegistrationListener(int slotId, int featureType,
-                IImsRegistrationListener listener) throws RemoteException {
-            enforceReadPhoneStatePermission("addRegistrationListener");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.addRegistrationListener(listener);
-                }
-            }
-        }
-
-        @Override
-        public void removeRegistrationListener(int slotId, int featureType,
-                IImsRegistrationListener listener) throws RemoteException {
-            enforceReadPhoneStatePermission("removeRegistrationListener");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.removeRegistrationListener(listener);
-                }
-            }
-        }
-
-        @Override
-        public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
-                int callSessionType, int callType) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.createCallProfile(sessionId, callSessionType,  callType);
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
-                ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.createCallSession(sessionId, profile, listener);
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
-                String callId) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.getPendingCallSession(sessionId, callId);
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public IImsUt getUtInterface(int slotId, int featureType)
-                throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.getUtInterface();
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public IImsConfig getConfigInterface(int slotId, int featureType)
-                throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.getConfigInterface();
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public void turnOnIms(int slotId, int featureType) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.turnOnIms();
-                }
-            }
-        }
-
-        @Override
-        public void turnOffIms(int slotId, int featureType) throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.turnOffIms();
-                }
-            }
-        }
-
-        @Override
-        public IImsEcbm getEcbmInterface(int slotId, int featureType)
-                throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.getEcbmInterface();
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
-                throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    feature.setUiTTYMode(uiTtyMode, onComplete);
-                }
-            }
-        }
-
-        @Override
-        public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
-                throws RemoteException {
-            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface");
-            synchronized (mFeatures) {
-                MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
-                if (feature != null) {
-                    return feature.getMultiEndpointInterface();
-                }
-            }
-            return null;
-        }
-
     };
 
     /**
@@ -341,127 +121,93 @@
     @Override
     public IBinder onBind(Intent intent) {
         if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService Bound.");
             return mImsServiceController;
         }
         return null;
     }
 
     /**
-     * Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and
-     * featureType
-     * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
-     * @param featureType An integer representing the type of ImsFeature being created. This is
-     * defined in {@link ImsFeature}.
+     * @hide
      */
-    // Be sure to lock on mFeatures before accessing this method
-    private void onCreateImsFeatureInternal(int slotId, int featureType,
+    @VisibleForTesting
+    public SparseArray<ImsFeature> getFeatures(int slotId) {
+        return mFeaturesBySlot.get(slotId);
+    }
+
+    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
-        if (featureMap == null) {
-            featureMap = new SparseArray<>();
-            mFeatures.put(slotId, featureMap);
-        }
-        ImsFeature f = makeImsFeature(slotId, featureType);
+        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
         if (f != null) {
-            f.setContext(this);
-            f.setSlotId(slotId);
-            f.addImsFeatureStatusCallback(c);
-            featureMap.put(featureType, f);
-        }
-
-    }
-    /**
-     * Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and
-     * featureType.
-     * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
-     * @param featureType An integer representing the type of ImsFeature being removed. This is
-     * defined in {@link ImsFeature}.
-     */
-    // Be sure to lock on mFeatures before accessing this method
-    private void onRemoveImsFeatureInternal(int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
-        if (featureMap == null) {
-            return;
-        }
-
-        ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType);
-        if (featureToRemove != null) {
-            featureMap.remove(featureType);
-            featureToRemove.notifyFeatureRemoved(slotId);
-            // Remove reference to Binder
-            featureToRemove.removeImsFeatureStatusCallback(c);
-        }
-    }
-
-    // Be sure to lock on mFeatures before accessing this method
-    private MMTelFeature resolveMMTelFeature(int slotId, int featureType) {
-        SparseArray<ImsFeature> features = getImsFeatureMap(slotId);
-        MMTelFeature feature = null;
-        if (features != null) {
-            feature = resolveImsFeature(features, featureType, MMTelFeature.class);
-        }
-        return feature;
-    }
-
-    // Be sure to lock on mFeatures before accessing this method
-    private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType,
-            Class<T> className) {
-        ImsFeature feature = getImsFeatureFromType(set, featureType);
-        if (feature == null) {
+            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            return f.getBinder();
+        } else {
             return null;
         }
-        try {
-            return className.cast(feature);
-        } catch (ClassCastException e)
-        {
-            Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage());
+    }
+
+    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
         }
-        return null;
     }
 
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    // Be sure to lock on mFeatures before accessing this method
-    public SparseArray<ImsFeature> getImsFeatureMap(int slotId) {
-        return mFeatures.get(slotId);
-    }
-
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    // Be sure to lock on mFeatures before accessing this method
-    public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) {
-        return set.get(featureType);
-    }
-
-    private ImsFeature makeImsFeature(int slotId, int feature) {
-        switch (feature) {
-            case ImsFeature.EMERGENCY_MMTEL: {
-                return onCreateEmergencyMMTelImsFeature(slotId);
-            }
-            case ImsFeature.MMTEL: {
-                return onCreateMMTelImsFeature(slotId);
-            }
-            case ImsFeature.RCS: {
-                return onCreateRcsFeature(slotId);
-            }
+    private IImsRcsFeature createRcsFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        RcsFeature f = onCreateRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.RCS, c);
+            return f.getBinder();
+        } else {
+            return null;
         }
-        // Tried to create feature that is not defined.
-        return null;
     }
 
-    /**
-     * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a
-     * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps.
-     */
-    private void enforceReadPhoneStatePermission(String fn) {
-        if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            enforceCallingOrSelfPermission(READ_PHONE_STATE, fn);
+    private void setupFeature(ImsFeature f, int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        f.setContext(this);
+        f.setSlotId(slotId);
+        f.addImsFeatureStatusCallback(c);
+        addImsFeature(slotId, featureType, f);
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.removeImsFeatureStatusCallback(c);
+            f.onFeatureRemoved();
+            features.remove(featureType);
         }
     }
 
@@ -470,7 +216,7 @@
      * functionality. Must be able to handle emergency calls at any time as well.
      * @hide
      */
-    public MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
         return null;
     }
 
@@ -479,7 +225,7 @@
      * functionality.
      * @hide
      */
-    public MMTelFeature onCreateMMTelImsFeature(int slotId) {
+    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
         return null;
     }
 
@@ -487,7 +233,7 @@
      * @return An implementation of RcsFeature that will be used by the system for RCS.
      * @hide
      */
-    public RcsFeature onCreateRcsFeature(int slotId) {
+    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
         return null;
     }
 }
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 9d880b7..062858d 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.Intent;
+import android.os.IInterface;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
@@ -91,17 +92,12 @@
     public static final int STATE_INITIALIZING = 1;
     public static final int STATE_READY = 2;
 
-    private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
     private @ImsState int mState = STATE_NOT_AVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
     private Context mContext;
 
-    public interface INotifyFeatureRemoved {
-        void onFeatureRemoved(int slotId);
-    }
-
     public void setContext(Context context) {
         mContext = context;
     }
@@ -110,26 +106,6 @@
         mSlotId = slotId;
     }
 
-    public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
-        synchronized (mRemovedListeners) {
-            mRemovedListeners.add(listener);
-        }
-    }
-
-    public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) {
-        synchronized (mRemovedListeners) {
-            mRemovedListeners.remove(listener);
-        }
-    }
-
-    // Not final for testing.
-    public void notifyFeatureRemoved(int slotId) {
-        synchronized (mRemovedListeners) {
-            mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId));
-            onFeatureRemoved();
-        }
-    }
-
     public int getFeatureState() {
         return mState;
     }
@@ -215,4 +191,9 @@
      * Called when the feature is being removed and must be cleaned up.
      */
     public abstract void onFeatureRemoved();
+
+    /**
+     * @return Binder instance
+     */
+    public abstract IInterface getBinder();
 }
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index 758c379..e790d14 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -18,18 +18,18 @@
 
 import android.app.PendingIntent;
 import android.os.Message;
+import android.os.RemoteException;
 
 import com.android.ims.ImsCallProfile;
 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.IImsUt;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.android.ims.internal.ImsCallSession;
 
 /**
  * Base implementation for MMTel.
@@ -41,6 +41,146 @@
 
 public class MMTelFeature extends ImsFeature {
 
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+
+    private final IImsMMTelFeature mImsMMTelBinder = new IImsMMTelFeature.Stub() {
+
+        @Override
+        public int startSession(PendingIntent incomingCallIntent,
+                IImsRegistrationListener listener) throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.startSession(incomingCallIntent, listener);
+            }
+        }
+
+        @Override
+        public void endSession(int sessionId) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.endSession(sessionId);
+            }
+        }
+
+        @Override
+        public boolean isConnected(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isConnected(callSessionType, callType);
+            }
+        }
+
+        @Override
+        public boolean isOpened() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isOpened();
+            }
+        }
+
+        @Override
+        public int getFeatureStatus() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getFeatureState();
+            }
+        }
+
+        @Override
+        public void addRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.addRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public void removeRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.removeRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallProfile(sessionId, callSessionType,  callType);
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+                IImsCallSessionListener listener) throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallSession(sessionId, profile, listener);
+            }
+        }
+
+        @Override
+        public IImsCallSession getPendingCallSession(int sessionId, String callId)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getPendingCallSession(sessionId, callId);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getUtInterface();
+            }
+        }
+
+        @Override
+        public IImsConfig getConfigInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getConfigInterface();
+            }
+        }
+
+        @Override
+        public void turnOnIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOnIms();
+            }
+        }
+
+        @Override
+        public void turnOffIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOffIms();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getEcbmInterface();
+            }
+        }
+
+        @Override
+        public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.setUiTTYMode(uiTtyMode, onComplete);
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getMultiEndpointInterface();
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMMTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+
     /**
      * Notifies the MMTel feature that you would like to start a session. This should always be
      * done before making/receiving IMS calls. The IMS service will register the device to the
@@ -135,7 +275,7 @@
     }
 
     /**
-     * Creates a {@link ImsCallSession} with the specified call profile.
+     * Creates an {@link ImsCallSession} with the specified call profile.
      * Use other methods, if applicable, instead of interacting with
      * {@link ImsCallSession} directly.
      *
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 332cca3..a82e608 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims.feature;
 
+import com.android.ims.internal.IImsRcsFeature;
+
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
  * this class and provide implementations of the RcsFeature methods that they support.
@@ -24,6 +26,11 @@
 
 public class RcsFeature extends ImsFeature {
 
+    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+        // Empty Default Implementation.
+    };
+
+
     public RcsFeature() {
         super();
     }
@@ -32,4 +39,9 @@
     public void onFeatureRemoved() {
 
     }
+
+    @Override
+    public final IImsRcsFeature getBinder() {
+        return mImsRcsBinder;
+    }
 }
diff --git a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
new file mode 100644
index 0000000..52b3853
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
@@ -0,0 +1,56 @@
+/*
+ * 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.internal;
+
+import android.app.PendingIntent;
+
+import com.android.ims.ImsCallProfile;
+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.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import android.os.Message;
+
+/**
+ * See MMTelFeature for more information.
+ * {@hide}
+ */
+interface IImsMMTelFeature {
+    int startSession(in PendingIntent incomingCallIntent,
+            in IImsRegistrationListener listener);
+    void endSession(int sessionId);
+    boolean isConnected(int callSessionType, int callType);
+    boolean isOpened();
+    int getFeatureStatus();
+    void addRegistrationListener(in IImsRegistrationListener listener);
+    void removeRegistrationListener(in IImsRegistrationListener listener);
+    ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType);
+    IImsCallSession createCallSession(int sessionId, in ImsCallProfile profile,
+            IImsCallSessionListener listener);
+    IImsCallSession getPendingCallSession(int sessionId, String callId);
+    IImsUt getUtInterface();
+    IImsConfig getConfigInterface();
+    void turnOnIms();
+    void turnOffIms();
+    IImsEcbm getEcbmInterface();
+    void setUiTTYMode(int uiTtyMode, in Message onComplete);
+    IImsMultiEndpoint getMultiEndpointInterface();
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsRcsFeature.aidl
similarity index 65%
copy from telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
copy to telephony/java/com/android/ims/internal/IImsRcsFeature.aidl
index df10700..b1cb23b 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRcsFeature.aidl
@@ -17,12 +17,9 @@
 package com.android.ims.internal;
 
 /**
- *  Interface from ImsResolver to ImsServiceProxy in ImsManager.
- * Callback to ImsManager when a feature changes in the ImsServiceController.
+ * See RcsFeature for more information.
  * {@hide}
  */
-oneway interface IImsServiceFeatureListener {
-    void imsFeatureCreated(int slotId, int feature);
-    void imsFeatureRemoved(int slotId, int feature);
-    void imsStatusChanged(int slotId, int feature, int status);
+interface IImsRcsFeature {
+    //Empty Default Implementation
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index f1e2262..857089f 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -16,49 +16,17 @@
 
 package com.android.ims.internal;
 
-import android.app.PendingIntent;
-
-import com.android.ims.ImsCallProfile;
-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.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsUt;
-
-import android.os.Message;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
 
 /**
  * See ImsService and MMTelFeature for more information.
  * {@hide}
  */
 interface IImsServiceController {
-    // ImsService Control
-    void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c);
-    void removeImsFeature(int slotId, int feature, IImsFeatureStatusCallback c);
-    // MMTel Feature
-    int startSession(int slotId, int featureType, in PendingIntent incomingCallIntent,
-            in IImsRegistrationListener listener);
-    void endSession(int slotId, int featureType, int sessionId);
-    boolean isConnected(int slotId, int featureType, int callSessionType, int callType);
-    boolean isOpened(int slotId, int featureType);
-    int getFeatureStatus(int slotId, int featureType);
-    void addRegistrationListener(int slotId, int featureType, in IImsRegistrationListener listener);
-    void removeRegistrationListener(int slotId, int featureType,
-            in IImsRegistrationListener listener);
-    ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
-            int callSessionType, int callType);
-    IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
-            in ImsCallProfile profile, IImsCallSessionListener listener);
-    IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
-            String callId);
-    IImsUt getUtInterface(int slotId, int featureType);
-    IImsConfig getConfigInterface(int slotId, int featureType);
-    void turnOnIms(int slotId, int featureType);
-    void turnOffIms(int slotId, int featureType);
-    IImsEcbm getEcbmInterface(int slotId, int featureType);
-    void setUiTTYMode(int slotId, int featureType, int uiTtyMode, in Message onComplete);
-    IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType);
+    IImsMMTelFeature createEmergencyMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
+    IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
+    IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
+    void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
similarity index 95%
rename from telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
rename to telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index df10700..9a9cf53 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -21,7 +21,7 @@
  * Callback to ImsManager when a feature changes in the ImsServiceController.
  * {@hide}
  */
-oneway interface IImsServiceFeatureListener {
+oneway interface IImsServiceFeatureCallback {
     void imsFeatureCreated(int slotId, int feature);
     void imsFeatureRemoved(int slotId, int feature);
     void imsStatusChanged(int slotId, int feature, int status);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3cc9bde..fd6091a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,8 +38,9 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
 
@@ -784,12 +785,27 @@
     int getTetherApnRequired();
 
     /**
-     *  Get ImsServiceController binder from ImsResolver that corresponds to the subId and feature
-     *  requested as well as registering the ImsServiceController for callbacks using the
-     *  IImsServiceFeatureListener interface.
+     *  Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
+     *  as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
+     *  interface.
      */
-    IImsServiceController getImsServiceControllerAndListen(int slotIndex, int feature,
-                IImsServiceFeatureListener callback);
+    IImsMMTelFeature getMMTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+
+    /**
+     *  Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
+     *  as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
+     *  interface.
+     *  Used for emergency calling only.
+     */
+    IImsMMTelFeature getEmergencyMMTelFeatureAndListen(int slotId,
+            in IImsServiceFeatureCallback callback);
+
+    /**
+     *  Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
+     *  as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback
+     *  interface.
+     */
+    IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
 
     /**
      * Set the network selection mode to automatic.