Check to ensure an ImsService can handle an IMS request

Previously, if there was no ImsService available at the time of an
IMS request, we would send a not available exception, which is
documented to have the caller retry. This is inefficient because it
will cause apps to retry indefinitely on devices that support IMS
but do not have an ImsService configured for the active subscription.

This change adds a check in ImsResolver to first make sure there is
an ImsService configured for the ImsFeature type that the request
is for and if there isn't, we will instead send a
ImsException#CODE_ERROR_UNSUPPORTED_OPERATION error, which is a
permanent failure that apps can use to stop retrying.

Fixes: 184188331
Test: atest CtsTelephonyTestCases
Change-Id: I1b7a50c320e5bca59659561c5a0cfd928cd6e1c0
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 2e4ee94..64e93a5 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -49,7 +49,6 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.ims.ImsResolver;
-import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.services.telephony.rcs.RcsFeatureController;
 import com.android.services.telephony.rcs.SipTransportController;
 import com.android.services.telephony.rcs.TelephonyRcsService;
@@ -214,6 +213,8 @@
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "unregisterRcsAvailabilityCallback: error=" + e.errorCode);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -338,12 +339,16 @@
     public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
             Set<String> featureTags) throws ImsException {
         // Permission check happening in PhoneInterfaceManager.
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return null;
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return null;
+            }
+            return uceCtrlManager.addUceRegistrationOverride(featureTags);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.addUceRegistrationOverride(featureTags);
     }
 
     /**
@@ -353,12 +358,16 @@
     public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
             Set<String> featureTags) throws ImsException {
         // Permission check happening in PhoneInterfaceManager.
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return null;
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return null;
+            }
+            return uceCtrlManager.removeUceRegistrationOverride(featureTags);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.removeUceRegistrationOverride(featureTags);
     }
 
     /**
@@ -367,13 +376,17 @@
     // Used for SHELL command only right now.
     public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId)
             throws ImsException {
-        // Permission check happening in PhoneInterfaceManager.
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return null;
+        try {
+            // Permission check happening in PhoneInterfaceManager.
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return null;
+            }
+            return uceCtrlManager.clearUceRegistrationOverride();
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.clearUceRegistrationOverride();
     }
 
     /**
@@ -382,13 +395,17 @@
     // Used for SHELL command only right now.
     public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId)
             throws ImsException {
-        // Permission check happening in PhoneInterfaceManager.
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return null;
+        try {
+            // Permission check happening in PhoneInterfaceManager.
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return null;
+            }
+            return uceCtrlManager.getLatestRcsContactUceCapability();
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.getLatestRcsContactUceCapability();
     }
 
     /**
@@ -397,14 +414,18 @@
      */
     // Used for SHELL command only right now.
     public String getLastUcePidfXmlShell(int subId) throws ImsException {
-        // Permission check happening in PhoneInterfaceManager.
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return null;
+        try {
+            // Permission check happening in PhoneInterfaceManager.
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return null;
+            }
+            String pidfXml = uceCtrlManager.getLastPidfXml();
+            return pidfXml == null ? "none" : pidfXml;
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        String pidfXml = uceCtrlManager.getLastPidfXml();
-        return pidfXml == null ? "none" : pidfXml;
     }
 
     /**
@@ -413,12 +434,16 @@
      */
     // Used for SHELL command only right now.
     public boolean removeUceRequestDisallowedStatus(int subId) throws ImsException {
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return false;
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return false;
+            }
+            return uceCtrlManager.removeUceRequestDisallowedStatus();
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.removeUceRequestDisallowedStatus();
     }
 
     /**
@@ -426,12 +451,16 @@
      */
     // Used for SHELL command only right now.
     public boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfter) throws ImsException {
-        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
-                UceControllerManager.class);
-        if (uceCtrlManager == null) {
-            return false;
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                return false;
+            }
+            return uceCtrlManager.setCapabilitiesRequestTimeout(timeoutAfter);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         }
-        return uceCtrlManager.setCapabilitiesRequestTimeout(timeoutAfter);
     }
 
     @Override
@@ -465,6 +494,8 @@
                         "This subscription does not support UCE.");
             }
             uceCtrlManager.unregisterPublishStateCallback(c);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "unregisterUcePublishStateCallback: error=" + e.errorCode);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -580,6 +611,8 @@
                 return;
             }
             transport.destroySipDelegate(subId, connection, reason);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "destroySipDelegate: error=" + e.errorCode);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -598,6 +631,8 @@
                 return;
             }
             transport.triggerFullNetworkRegistration(subId, connection, sipCode, sipReason);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "triggerNetworkRegistration: error=" + e.errorCode);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -688,31 +723,6 @@
     }
 
     /**
-     * Retrieve ImsPhone instance.
-     *
-     * @param subId the subscription ID
-     * @return The ImsPhone instance
-     * @throws ServiceSpecificException if getting ImsPhone instance failed.
-     */
-    private ImsPhone getImsPhone(int subId) {
-        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS is not available on device.");
-        }
-        Phone phone = PhoneGlobals.getPhone(subId);
-        if (phone == null) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
-                    "Invalid subscription Id: " + subId);
-        }
-        ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
-        if (imsPhone == null) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
-                    "Cannot find ImsPhone instance: " + subId);
-        }
-        return imsPhone;
-    }
-
-    /**
      * Retrieve RcsFeatureManager instance.
      *
      * @param subId the subscription ID
@@ -734,6 +744,7 @@
                     "Invalid subscription Id: " + subId);
         }
         int slotId = phone.getPhoneId();
+        verifyImsRcsConfiguredOrThrow(slotId);
         RcsFeatureController c = mRcsService.getFeatureController(slotId);
         if (c == null) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -742,6 +753,20 @@
         return c;
     }
 
+    /**
+     * Throw an ImsException if the IMS resolver does not have an ImsService configured for RCS
+     * for the given slot ID or no ImsResolver instance has been created.
+     * @param slotId The slot ID that the IMS service is created for.
+     * @throws ServiceSpecificException If there is no ImsService configured for this slot.
+     */
+    private void verifyImsRcsConfiguredOrThrow(int slotId) {
+        if (mImsResolver == null
+                || !mImsResolver.isImsServiceConfiguredForFeature(slotId, ImsFeature.FEATURE_RCS)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "This subscription does not support RCS");
+        }
+    }
+
     private boolean isImsSingleRegistrationSupportedOnDevice() {
         return mSingleRegistrationOverride != null ? mSingleRegistrationOverride
                 : mApp.getPackageManager().hasSystemFeature(
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ba3d138..906d4c3 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3845,9 +3845,9 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
-                    .addRegistrationCallbackForSubscription(c, subId);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).addRegistrationCallbackForSubscription(c, subId);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -3869,7 +3869,6 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            // TODO(b/159910732): Remove ImsManager dependence and query through ImsPhone directly.
             ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                     .removeRegistrationCallbackForSubscription(c, subId);
         } catch (ImsException e) {
@@ -3965,11 +3964,11 @@
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "IMS not available on device.");
         }
-        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
         final long token = Binder.clearCallingIdentity();
         try {
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
-                    .addCapabilitiesCallbackForSubscription(c, subId);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).addCapabilitiesCallbackForSubscription(c, subId);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -3992,7 +3991,6 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            // TODO(b/159910732): Remove ImsManager dependence and query through ImsPhone directly.
             ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                         .removeCapabilitiesCallbackForSubscription(c, subId);
         } catch (ImsException e) {
@@ -4008,11 +4006,11 @@
     @Override
     public boolean isCapable(int subId, int capability, int regTech) {
         enforceReadPrivilegedPermission("isCapable");
-        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
         final long token = Binder.clearCallingIdentity();
         try {
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).queryMmTelCapability(capability, regTech);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).queryMmTelCapability(capability, regTech);
         } catch (com.android.ims.ImsException e) {
             Log.w(LOG_TAG, "IMS isCapable - service unavailable: " + e.getMessage());
             return false;
@@ -4064,6 +4062,7 @@
                         + subId + "'");
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
             }
+            verifyImsMmTelConfiguredOrThrow(slotId);
             ImsManager.getInstance(mApp, slotId).isSupported(capability,
                     transportType, aBoolean -> {
                         try {
@@ -4073,6 +4072,8 @@
                                     + "running. Ignore");
                         }
                     });
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -4087,11 +4088,11 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
-        // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
         final long token = Binder.clearCallingIdentity();
         try {
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).isEnhanced4gLteModeSettingEnabledByUser();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isEnhanced4gLteModeSettingEnabledByUser();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4105,9 +4106,9 @@
                 "setAdvancedCallingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setEnhanced4gLteModeSetting(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setEnhanced4gLteModeSetting(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4125,8 +4126,9 @@
                 mApp, subId, "isVtSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp, getSlotIndexOrException(subId)).isVtEnabledByUser();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isVtEnabledByUser();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4140,8 +4142,9 @@
                 "setVtSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId)).setVtSetting(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setVtSetting(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4159,9 +4162,9 @@
                 mApp, subId, "isVoWiFiSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).isWfcEnabledByUser();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isWfcEnabledByUser();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4175,8 +4178,9 @@
                 "setVoWiFiSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId)).setWfcSetting(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setWfcSetting(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4195,9 +4199,9 @@
                 mApp, subId, "isCrossSimCallingEnabledByUser");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).isCrossSimCallingEnabledByUser();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isCrossSimCallingEnabledByUser();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4218,9 +4222,9 @@
                 "setCrossSimCallingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
-                    .setCrossSimCallingEnabled(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setCrossSimCallingEnabled(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4239,9 +4243,9 @@
                 mApp, subId, "isVoWiFiRoamingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).isWfcRoamingEnabledByUser();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isWfcRoamingEnabledByUser();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4255,9 +4259,9 @@
                 "setVoWiFiRoamingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcRoamingSetting(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setWfcRoamingSetting(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4271,9 +4275,9 @@
                 "setVoWiFiNonPersistent");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setWfcNonPersistent(isCapable, mode);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4291,9 +4295,9 @@
                 mApp, subId, "getVoWiFiModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getWfcMode(false /*isRoaming*/);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).getWfcMode(false /*isRoaming*/);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4307,9 +4311,9 @@
                 "setVoWiFiModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcMode(mode, false /*isRoaming*/);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setWfcMode(mode, false /*isRoaming*/);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4322,9 +4326,9 @@
         enforceReadPrivilegedPermission("getVoWiFiRoamingModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getWfcMode(true /*isRoaming*/);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).getWfcMode(true /*isRoaming*/);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4338,9 +4342,9 @@
                 "setVoWiFiRoamingModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcMode(mode, true /*isRoaming*/);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setWfcMode(mode, true /*isRoaming*/);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4354,8 +4358,9 @@
                 "setRttCapabilityEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId)).setRttEnabled(isEnabled);
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId).setRttEnabled(isEnabled);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4373,9 +4378,9 @@
                 mApp, subId, "isTtyOverVolteEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).isTtyOnVoLteCapable();
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            return ImsManager.getInstance(mApp, slotId).isTtyOnVoLteCapable();
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -4392,8 +4397,9 @@
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                         "IMS not available on device.");
             }
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+            ImsManager.getInstance(mApp, slotId)
                     .addProvisioningCallbackForSubscription(callback, subId);
         } catch (ImsException e) {
             throw new ServiceSpecificException(e.getCode());
@@ -4410,7 +4416,6 @@
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
         try {
-            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
             ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                     .removeProvisioningCallbackForSubscription(callback, subId);
         } catch (ImsException e) {
@@ -4857,6 +4862,20 @@
         }
     }
 
+    /**
+     * Throw an ImsException if the IMS resolver does not have an ImsService configured for MMTEL
+     * for the given slot ID or no ImsResolver instance has been created.
+     * @param slotId The slot ID that the IMS service is created for.
+     * @throws ImsException If there is no ImsService configured for this slot.
+     */
+    private void verifyImsMmTelConfiguredOrThrow(int slotId) throws ImsException {
+        if (mImsResolver == null || !mImsResolver.isImsServiceConfiguredForFeature(slotId,
+                ImsFeature.FEATURE_MMTEL)) {
+            throw new ImsException("This subscription does not support MMTEL over IMS",
+                    ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+        }
+    }
+
     private int getSlotIndexOrException(int subId) throws ImsException {
         int slotId = SubscriptionManager.getSlotIndex(subId);
         if (!SubscriptionManager.isValidSlotIndex(slotId)) {
@@ -5817,6 +5836,7 @@
                         + subId + "'");
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
             }
+            verifyImsMmTelConfiguredOrThrow(slotId);
             ImsManager.getInstance(mApp, slotId).getImsServiceState(anInteger -> {
                 try {
                     callback.accept(anInteger == null ? ImsFeature.STATE_UNAVAILABLE : anInteger);
@@ -5825,6 +5845,8 @@
                             + "Ignore");
                 }
             });
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -10079,8 +10101,8 @@
         try {
             if (!RcsProvisioningMonitor.getInstance()
                     .registerRcsProvisioningCallback(subId, callback)) {
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                        "Service not available for the subscription.");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
+                        "Active subscription not found.");
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -10161,11 +10183,15 @@
             IImsConfig configBinder = getImsConfig(getSlotIndex(subId), ImsFeature.FEATURE_RCS);
             if (configBinder == null) {
                 Rlog.e(LOG_TAG, "null result for setRcsClientConfiguration");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
+                        "could not find the requested subscription");
             } else {
                 configBinder.setRcsClientConfiguration(rcc);
             }
         } catch (RemoteException e) {
             Rlog.e(LOG_TAG, "fail to setRcsClientConfiguration " + e.getMessage());
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                    "service is temporarily unavailable.");
         } finally {
             Binder.restoreCallingIdentity(identity);
         }