Merge "Dial CS calls directly in limited service" into main
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 58f2670..801eb12 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -862,7 +862,7 @@
     <string name="radioInfo_phone_offhook" msgid="7564601639749936170">"Opkald i gang"</string>
     <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Afbrudt"</string>
     <string name="radioInfo_data_connecting" msgid="925092271092152472">"Forbindelsen oprettes"</string>
-    <string name="radioInfo_data_connected" msgid="7637335645634239508">"Tilsluttet"</string>
+    <string name="radioInfo_data_connected" msgid="7637335645634239508">"Forbundet"</string>
     <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspenderet"</string>
     <string name="radioInfo_unknown" msgid="5401423738500672850">"Ukendt"</string>
     <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primær"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 4f8e559..febd572 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -165,7 +165,7 @@
     <string name="voicemail_default" msgid="6427575113775462077">"आपको सेवा देने वाली कंपनी"</string>
     <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"पुराना पिन"</string>
     <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"नया पिन"</string>
-    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"कृपया प्रतीक्षा करें."</string>
+    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"कृपया इंतज़ार करें."</string>
     <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"नया पिन बहुत छोटा है."</string>
     <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"नया पिन बहुत बड़ा है."</string>
     <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"नया पिन बहुत कमज़ोर है. किसी सशक्त पासवर्ड में निरंतर क्रम या अंकों का दोहराव नहीं होना चाहिए."</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 5eea570..10c516c 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -19,7 +19,7 @@
     <string name="phoneAppLabel" product="tablet" msgid="1916019789885839910">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
     <string name="phoneAppLabel" product="default" msgid="130465039375347763">"ଫୋନ୍ ସେବା"</string>
     <string name="emergencyDialerIconLabel" msgid="8668005772339436680">"ଜରୁରୀକାଳିନ ଡାଏଲର୍"</string>
-    <string name="phoneIconLabel" msgid="3015941229249651419">"ଫୋନ୍"</string>
+    <string name="phoneIconLabel" msgid="3015941229249651419">"ଫୋନ"</string>
     <string name="fdnListLabel" msgid="4119121875004244097">"FDN ତାଲିକା"</string>
     <string name="unknown" msgid="8279698889921830815">"ଅଜଣା"</string>
     <string name="private_num" msgid="4487990167889159992">"ବ୍ୟକ୍ତିଗତ ନମ୍ବର୍"</string>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3eafb24..ef71016 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -16,12 +16,15 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.KEY_CONFIG_BUNDLE;
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.RESULT_ERROR;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -66,6 +69,7 @@
 import com.android.internal.telephony.PhoneConfigurationManager;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.telephony.util.TelephonyUtils;
@@ -685,12 +689,17 @@
 
     @NonNull private final Handler mHandler;
 
+    @NonNull private final FeatureFlags  mFeatureFlags;
+
+    @NonNull private final PackageManager mPackageManager;
+
     /**
      * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
      * receiver for relevant events.
      */
     @VisibleForTesting
-    /* package */ CarrierConfigLoader(@NonNull Context context, @NonNull Looper looper) {
+    /* package */ CarrierConfigLoader(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags) {
         super(PermissionEnforcer.fromContext(context));
         mContext = context;
         mPlatformCarrierConfigPackage =
@@ -719,6 +728,8 @@
             TelephonyManager.from(context).registerCarrierPrivilegesCallback(phoneId,
                     new HandlerExecutor(mHandler), mCarrierServiceChangeCallbacks[phoneId]);
         }
+        mFeatureFlags = featureFlags;
+        mPackageManager = context.getPackageManager();
         logd("CarrierConfigLoader has started");
 
         PhoneConfigurationManager.registerForMultiSimConfigChange(
@@ -733,10 +744,11 @@
      * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
      */
     @NonNull
-    /* package */ static CarrierConfigLoader init(@NonNull Context context) {
+    /* package */ static CarrierConfigLoader init(@NonNull Context context,
+            @NonNull FeatureFlags featureFlags) {
         synchronized (CarrierConfigLoader.class) {
             if (sInstance == null) {
-                sInstance = new CarrierConfigLoader(context, Looper.myLooper());
+                sInstance = new CarrierConfigLoader(context, Looper.myLooper(), featureFlags);
                 // Make this service available through ServiceManager.
                 TelephonyFrameworkInitializer.getTelephonyServiceManager()
                         .getCarrierConfigServiceRegisterer().register(sInstance);
@@ -1323,6 +1335,8 @@
             return new PersistableBundle();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage, "getConfigForSubIdWithFeature");
+
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
@@ -1367,6 +1381,9 @@
         Objects.requireNonNull(keys, "Config keys must be non-null");
         enforceCallerIsSystemOrRequestingPackage(callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                "getConfigSubsetForSubIdWithFeature");
+
         // Permission check is performed inside and an empty bundle will return on failure.
         // No SecurityException thrown here since most clients expect to retrieve the overridden
         // value if present or use default one if not
@@ -1416,6 +1433,9 @@
             throw new IllegalArgumentException(
                     "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(), "overrideConfig");
+
         // Post to run on handler thread on which all states should be confined.
         mHandler.post(() -> {
             overrideConfig(mOverrideConfigs, phoneId, overrides);
@@ -1468,6 +1488,9 @@
                     "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                "notifyConfigChangedForSubId");
+
         logdWithLocalLog("Notified carrier config changed. phoneId=" + phoneId
                 + ", subId=" + subscriptionId);
 
@@ -1488,6 +1511,9 @@
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(), "updateConfigForPhoneId");
+
         // requires Java 7 for switch on string.
         switch (simState) {
             case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
@@ -1509,6 +1535,10 @@
     @NonNull
     public String getDefaultCarrierServicePackageName() {
         getDefaultCarrierServicePackageName_enforcePermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                "getDefaultCarrierServicePackageName");
+
         return mPlatformCarrierConfigPackage;
     }
 
@@ -1819,6 +1849,40 @@
                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
     }
 
+    /**
+     * Get the current calling package name.
+     * @return the current calling package name
+     */
+    @Nullable
+    private String getCurrentPackageName() {
+        if (mPackageManager == null) return null;
+        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingUids == null) ? null : callingUids[0];
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())) {
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + FEATURE_TELEPHONY_SUBSCRIPTION);
+        }
+    }
+
     private class CarrierServiceConnection implements ServiceConnection {
         final int phoneId;
         @NonNull final String pkgName;
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 3f35454..a778f6a 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,7 +16,13 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
+
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -53,6 +59,7 @@
 import com.android.internal.telephony.ISipDialogStateCallback;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.services.telephony.rcs.RcsFeatureController;
 import com.android.services.telephony.rcs.SipTransportController;
@@ -74,6 +81,8 @@
     private PhoneGlobals mApp;
     private TelephonyRcsService mRcsService;
     private ImsResolver mImsResolver;
+    private FeatureFlags mFeatureFlags;
+    private PackageManager mPackageManager;
     // set by shell cmd phone src set-device-enabled true/false
     private Boolean mSingleRegistrationOverride;
 
@@ -90,10 +99,10 @@
      * Initialize the singleton ImsRcsController instance.
      * This is only done once, at startup, from PhoneApp.onCreate().
      */
-    static ImsRcsController init(PhoneGlobals app) {
+    static ImsRcsController init(PhoneGlobals app, FeatureFlags featureFlags) {
         synchronized (ImsRcsController.class) {
             if (sInstance == null) {
-                sInstance = new ImsRcsController(app);
+                sInstance = new ImsRcsController(app, featureFlags);
             } else {
                 Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -102,9 +111,11 @@
     }
 
     /** Private constructor; @see init() */
-    private ImsRcsController(PhoneGlobals app) {
+    private ImsRcsController(PhoneGlobals app, FeatureFlags featureFlags) {
         Log.i(TAG, "ImsRcsController");
         mApp = app;
+        mFeatureFlags = featureFlags;
+        mPackageManager = mApp.getPackageManager();
         TelephonyFrameworkInitializer
                 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
         mImsResolver = ImsResolver.getInstance();
@@ -118,6 +129,10 @@
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerImsRegistrationCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
@@ -136,6 +151,10 @@
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterImsRegistrationCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterImsRegistrationCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
@@ -153,6 +172,10 @@
     public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationState");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsRcsRegistrationState");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationState(regState -> {
@@ -175,6 +198,10 @@
     public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationTransportType");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsRcsRegistrationTransportType");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationTech(regTech -> {
@@ -204,6 +231,10 @@
     @Override
     public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerRcsAvailabilityCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
@@ -224,6 +255,10 @@
     @Override
     public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterRcsAvailabilityCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
@@ -247,6 +282,10 @@
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         enforceReadPrivilegedPermission("isCapable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCapable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return getRcsFeatureController(subId).isCapable(capability, radioTech);
@@ -273,6 +312,10 @@
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         enforceReadPrivilegedPermission("isAvailable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAvailable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return getRcsFeatureController(subId).isAvailable(capability, radioTech);
@@ -290,6 +333,10 @@
             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
         enforceAccessUserCapabilityExchangePermission("requestCapabilities");
         enforceReadContactsPermission("requestCapabilities");
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "requestCapabilities");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -311,6 +358,10 @@
             String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
         enforceAccessUserCapabilityExchangePermission("requestAvailability");
         enforceReadContactsPermission("requestAvailability");
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "requestAvailability");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -330,6 +381,10 @@
     @Override
     public @PublishState int getUcePublishState(int subId) {
         enforceReadPrivilegedPermission("getUcePublishState");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getUcePublishState");
+
         final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         boolean isSupportPublishingState = false;
@@ -485,6 +540,10 @@
     @Override
     public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerUcePublishStateCallback");
+
         final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         boolean isSupportPublishingState = false;
@@ -510,6 +569,10 @@
     @Override
     public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterUcePublishStateCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -534,6 +597,10 @@
                     + "isUceSettingEnabled");
             return false;
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "isUceSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return SubscriptionManager.getBooleanSubscriptionProperty(subId,
@@ -546,6 +613,10 @@
     @Override
     public void setUceSettingEnabled(int subId, boolean isEnabled) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setUceSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             SubscriptionManager.setSubscriptionProperty(subId,
@@ -680,6 +751,10 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "registerSipDialogStateCallback");
+
         try {
             SipTransportController transport = getRcsFeatureController(subId).getFeature(
                     SipTransportController.class);
@@ -707,6 +782,10 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "unregisterSipDialogStateCallback");
+
         try {
             SipTransportController transport = getRcsFeatureController(subId).getFeature(
                     SipTransportController.class);
@@ -897,6 +976,40 @@
                         PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
     }
 
+    /**
+     * Get the current calling package name.
+     * @return the current calling package name
+     */
+    @Nullable
+    private String getCurrentPackageName() {
+        if (mPackageManager == null) return null;
+        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingUids == null) ? null : callingUids[0];
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String telephonyFeature, @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())) {
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(telephonyFeature)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + telephonyFeature);
+        }
+    }
+
     void setRcsService(TelephonyRcsService rcsService) {
         mRcsService = rcsService;
     }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index f93b1e5..2f372ce 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -576,9 +576,9 @@
 
             phoneMgr = PhoneInterfaceManager.init(this, mFeatureFlags);
 
-            imsRcsController = ImsRcsController.init(this);
+            imsRcsController = ImsRcsController.init(this, mFeatureFlags);
 
-            configLoader = CarrierConfigLoader.init(this);
+            configLoader = CarrierConfigLoader.init(this, mFeatureFlags);
 
             if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
                 mImsStateCallbackController =
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 8f3281c..01ba40a 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -16,8 +16,11 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.permission.flags.Flags.opEnableMobileDataByUser;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
 
@@ -409,7 +412,7 @@
     private static List<String> sThermalMitigationAllowlistedPackages = new ArrayList<>();
 
     private final PhoneGlobals mApp;
-    private final FeatureFlags mFeatureFlags;
+    private FeatureFlags mFeatureFlags;
     private final CallManager mCM;
     private final ImsResolver mImsResolver;
 
@@ -420,11 +423,11 @@
     private final SharedPreferences mTelephonySharedPreferences;
     private final PhoneConfigurationManager mPhoneConfigurationManager;
     private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities;
+    private PackageManager mPackageManager;
 
     /** User Activity */
     private final AtomicBoolean mNotifyUserActivity;
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
-
     private final Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
 
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
@@ -2460,6 +2463,7 @@
         mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
         mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
         mNotifyUserActivity = new AtomicBoolean(false);
+        mPackageManager = app.getPackageManager();
         PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
         publish();
         CarrierAllowListInfo.loadInstance(mApp);
@@ -2554,6 +2558,9 @@
     }
 
     public void dial(String number) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "dial");
+
         dialForSubscriber(getPreferredVoiceSubscription(), number);
     }
 
@@ -2599,6 +2606,9 @@
             return;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "call");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             String url = createTelUrl(number);
@@ -2642,6 +2652,10 @@
     public int[] supplyPinReportResultForSubscriber(int subId, String pin) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "supplyPinReportResultForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -2656,6 +2670,9 @@
     public int[] supplyPukReportResultForSubscriber(int subId, String puk, String pin) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "supplyPukForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -2861,6 +2878,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "isRadioOnWithFeature");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return isRadioOnForSubscriber(subId);
@@ -2890,6 +2910,9 @@
     public void toggleRadioOnOffForSubscriber(int subId) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "toggleRadioOnOffForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -2925,6 +2948,10 @@
 
     public boolean needMobileRadioShutdown() {
         enforceReadPrivilegedPermission("needMobileRadioShutdown");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "needMobileRadioShutdown");
+
         /*
          * If any of the Radios are available, it will need to be
          * shutdown. So return true if any Radio is available.
@@ -2946,6 +2973,9 @@
     public void shutdownMobileRadios() {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "shutdownMobileRadios");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
@@ -3027,6 +3057,9 @@
             @TelephonyManager.RadioPowerReason int reason) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestRadioPowerOffForReason");
+
         log("requestRadioPowerOffForReason: subId=" + subId
                 + ",reason=" + reason + ",callingPackage=" + getCurrentPackageName());
         final long identity = Binder.clearCallingIdentity();
@@ -3057,6 +3090,9 @@
             @TelephonyManager.RadioPowerReason int reason) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "clearRadioPowerOffForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             boolean result = false;
@@ -3084,6 +3120,9 @@
     public List getRadioPowerOffReasons(int subId, String callingPackage, String callingFeatureId) {
         enforceReadPrivilegedPermission("getRadioPowerOffReasons");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioPowerOffReasons");
+
         final long identity = Binder.clearCallingIdentity();
         List result = new ArrayList();
         try {
@@ -3109,6 +3148,9 @@
     public boolean enableDataConnectivity(String callingPackage) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "enableDataConnectivity");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3130,6 +3172,9 @@
     public boolean disableDataConnectivity(String callingPackage) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "disableDataConnectivity");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3148,6 +3193,9 @@
 
     @Override
     public boolean isDataConnectivityPossible(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataConnectivityPossible");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3168,6 +3216,9 @@
     public void handleUssdRequest(int subId, String ussdRequest, ResultReceiver wrappedCallback) {
         enforceCallPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "handleUssdRequest");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3183,6 +3234,9 @@
     public boolean handlePinMmiForSubscriber(int subId, String dialString) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "handlePinMmiForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3230,6 +3284,10 @@
                         + "targeting API level 31+.");
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallStateForSubscription");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -3247,6 +3305,9 @@
 
     @Override
     public int getDataStateForSubId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataStateForSubId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3268,6 +3329,9 @@
 
     @Override
     public @DataActivityType int getDataActivityForSubId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataActivityForSubId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3305,6 +3369,9 @@
                         ? new CellIdentityCdma() : new CellIdentityGsm();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getCellLocation");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -3318,6 +3385,9 @@
 
     @Override
     public String getNetworkCountryIsoForPhone(int phoneId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkCountryIsoForPhone");
+
         // Reporting the correct network country is ambiguous when IWLAN could conflict with
         // registered cell info, so return a NULL country instead.
         final long identity = Binder.clearCallingIdentity();
@@ -3388,6 +3458,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNeighboringCellInfo");
+
         if (DBG_LOC) log("getNeighboringCellInfo: is active user");
 
         List<CellInfo> info = getAllCellInfo(callingPackage, callingFeatureId);
@@ -3441,6 +3514,9 @@
             return getCachedCellInfo();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllCellInfo");
+
         if (DBG_LOC) log("getAllCellInfo: is active user");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
@@ -3509,6 +3585,8 @@
                 return;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestCellInfoUpdateInternal");
 
         final Phone phone = getPhoneFromSubId(subId);
         if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
@@ -3547,6 +3625,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_GSM, "getImeiForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getImei();
@@ -3562,6 +3643,10 @@
                 callingFeatureId, "getPrimaryImei")) {
             throw new SecurityException("Caller does not have permission");
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_GSM, "getPrimaryImei");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone : PhoneFactory.getPhones()) {
@@ -3577,6 +3662,9 @@
 
     @Override
     public String getTypeAllocationCodeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_GSM, "getTypeAllocationCodeForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
         String tac = null;
         if (phone != null) {
@@ -3611,6 +3699,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getMeidForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getMeid();
@@ -3621,6 +3712,9 @@
 
     @Override
     public String getManufacturerCodeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getManufacturerCodeForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
         String manufacturerCode = null;
         if (phone != null) {
@@ -3650,6 +3744,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "getDeviceSoftwareVersionForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getDeviceSvn();
@@ -3660,6 +3757,9 @@
 
     @Override
     public int getSubscriptionCarrierId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionCarrierId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3671,6 +3771,9 @@
 
     @Override
     public String getSubscriptionCarrierName(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionCarrierName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3682,6 +3785,9 @@
 
     @Override
     public int getSubscriptionSpecificCarrierId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionSpecificCarrierId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3694,6 +3800,10 @@
 
     @Override
     public String getSubscriptionSpecificCarrierName(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getSubscriptionSpecificCarrierName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3712,6 +3822,10 @@
         if (phone == null) {
             return TelephonyManager.UNKNOWN_CARRIER_ID;
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierIdFromMccMnc");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return CarrierResolver.getCarrierIdFromMccMnc(phone.getContext(), mccmnc);
@@ -3832,6 +3946,9 @@
 
     @Override
     public int getActivePhoneTypeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "getActivePhoneTypeForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -3863,6 +3980,10 @@
             return -1;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CDMA,
+                "getCdmaEriIconIndexForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3948,6 +4069,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaMdn");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaMdn");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3970,6 +4094,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaMin");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaMin");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3999,6 +4126,9 @@
                     + ", configured package: " + authorizedPackage);
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "requestNumberVerification");
+
         if (range == null) {
             throw new NullPointerException("Range must be non-null");
         }
@@ -4013,6 +4143,9 @@
      * Returns true if CDMA provisioning needs to run.
      */
     public boolean needsOtaServiceProvisioning() {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "needsOtaServiceProvisioning");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getDefaultPhone().needsOtaServiceProvisioning();
@@ -4029,6 +4162,9 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
                 mApp, subId, "setVoiceMailNumber");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoiceMailNumber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Boolean success = (Boolean) sendRequest(CMD_SET_VOICEMAIL_NUMBER,
@@ -4048,6 +4184,9 @@
             throw new SecurityException("caller must be system dialer");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVisualVoicemailSettings");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             PhoneAccountHandle phoneAccountHandle = PhoneAccountHandleConverter.fromSubId(subId);
@@ -4070,6 +4209,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVisualVoicemailPackageName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return RemoteVvmTaskManager.getRemotePackage(mApp, subId).getPackageName();
@@ -4083,6 +4225,9 @@
             VisualVoicemailSmsFilterSettings settings) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "enableVisualVoicemailSmsFilter");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             VisualVoicemailSmsFilterConfig.enableVisualVoicemailSmsFilter(
@@ -4096,6 +4241,9 @@
     public void disableVisualVoicemailSmsFilter(String callingPackage, int subId) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "disableVisualVoicemailSmsFilter");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             VisualVoicemailSmsFilterConfig.disableVisualVoicemailSmsFilter(
@@ -4139,6 +4287,10 @@
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         enforceVisualVoicemailPackage(callingPackage, subId);
         enforceSendSmsPermission();
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "sendVisualVoicemailSmsForSubscriber");
+
         SmsController smsController = PhoneFactory.getSmsController();
         smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
                 subId, number, port, text, sentIntent);
@@ -4152,6 +4304,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setVoiceActivationState");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoiceActivationState");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -4173,6 +4328,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setDataActivationState");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataActivationState");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -4193,6 +4351,9 @@
     public int getVoiceActivationState(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getVoiceActivationState");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoiceActivationState");
+
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4213,6 +4374,9 @@
     public int getDataActivationState(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getDataActivationState");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataActivationState");
+
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4256,6 +4420,9 @@
      */
     @Override
     public boolean isConcurrentVoiceAndDataAllowed(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isConcurrentVoiceAndDataAllowed");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).isConcurrentVoiceAndDataAllowed();
@@ -4280,6 +4447,9 @@
                     getDefaultSubscription(), "sendDialerSpecialCode");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "sendDialerSpecialCode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             defaultPhone.sendDialerSpecialCode(inputCode);
@@ -4293,6 +4463,10 @@
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getNetworkSelectionMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkSelectionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -4307,6 +4481,10 @@
     @Override
     public boolean isInEmergencySmsMode() {
         enforceReadPrivilegedPermission("isInEmergencySmsMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "isInEmergencySmsMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone : PhoneFactory.getPhones()) {
@@ -4601,6 +4779,10 @@
     @Override
     public boolean isCapable(int subId, int capability, int regTech) {
         enforceReadPrivilegedPermission("isCapable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCapable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4620,6 +4802,10 @@
     @Override
     public boolean isAvailable(int subId, int capability, int regTech) {
         enforceReadPrivilegedPermission("isAvailable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAvailable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -4683,6 +4869,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAdvancedCallingSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4699,6 +4888,10 @@
     public void setAdvancedCallingSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setAdvancedCallingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setAdvancedCallingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4720,6 +4913,10 @@
     public boolean isVtSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVtSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVtSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4736,6 +4933,10 @@
     public void setVtSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVtSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVtSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4757,6 +4958,10 @@
     public boolean isVoWiFiSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVoWiFiSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4773,6 +4978,10 @@
     public void setVoWiFiSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4795,6 +5004,10 @@
     public boolean isCrossSimCallingEnabledByUser(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isCrossSimCallingEnabledByUser");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCrossSimCallingEnabledByUser");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4818,6 +5031,10 @@
     public void setCrossSimCallingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setCrossSimCallingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setCrossSimCallingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4840,6 +5057,10 @@
     public boolean isVoWiFiRoamingSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiRoamingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVoWiFiRoamingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4856,6 +5077,10 @@
     public void setVoWiFiRoamingSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiRoamingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiRoamingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4873,6 +5098,10 @@
     public void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiNonPersistent");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiNonPersistent");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4893,6 +5122,10 @@
     public int getVoWiFiModeSetting(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getVoWiFiModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getVoWiFiModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4909,6 +5142,10 @@
     public void setVoWiFiModeSetting(int subId, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4925,6 +5162,10 @@
     @Override
     public int getVoWiFiRoamingModeSetting(int subId) {
         enforceReadPrivilegedPermission("getVoWiFiRoamingModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getVoWiFiRoamingModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4941,6 +5182,10 @@
     public void setVoWiFiRoamingModeSetting(int subId, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiRoamingModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiRoamingModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4958,6 +5203,10 @@
     public void setRttCapabilitySetting(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setRttCapabilityEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setRttCapabilitySetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4979,6 +5228,10 @@
     public boolean isTtyOverVolteEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isTtyOverVolteEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isTtyOverVolteEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -5104,6 +5357,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5124,6 +5380,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5144,6 +5403,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5163,6 +5425,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5183,6 +5448,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5203,6 +5471,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isRcsProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5226,6 +5497,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsProvisioningInt");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5266,6 +5540,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsProvisioningString");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningString");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5293,6 +5570,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningInt");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5332,6 +5612,10 @@
         }
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningString");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningString");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5405,6 +5689,9 @@
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5442,6 +5729,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getDataNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5470,6 +5760,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoiceNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5497,6 +5790,9 @@
      */
     @Override
     public boolean hasIccCardUsingSlotIndex(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "hasIccCardUsingSlotIndex");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -5534,6 +5830,9 @@
             return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getLteOnCdmaModeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5615,6 +5914,7 @@
     @Override
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(
             @NonNull IccLogicalChannelRequest request) {
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/ "iccOpenLogicalChannel");
 
@@ -5622,6 +5922,9 @@
         // Verify that the callingPackage in the request belongs to the calling UID
         mAppOps.checkPackage(Binder.getCallingUid(), request.callingPackage);
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccOpenLogicalChannel");
+
         return iccOpenLogicalChannelWithPermission(phone, request);
     }
 
@@ -5668,6 +5971,9 @@
 
     @Override
     public boolean iccCloseLogicalChannel(@NonNull IccLogicalChannelRequest request) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccCloseLogicalChannel");
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/"iccCloseLogicalChannel");
 
@@ -5715,6 +6021,10 @@
             int command, int p1, int p2, int p3, String data) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccTransmitApduLogicalChannel");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduLogicalChannel");
+
         if (DBG) {
             log("iccTransmitApduLogicalChannel: subId=" + subId + " chnl=" + channel
                     + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3="
@@ -5728,6 +6038,11 @@
     public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
             int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "iccTransmitApduLogicalChannelBySlot");
+
         if (DBG) {
             log("iccTransmitApduLogicalChannelByPort: slotIndex=" + slotIndex + " portIndex="
                     + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
@@ -5769,6 +6084,10 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccTransmitApduBasicChannel");
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduBasicChannel");
+
         if (DBG) {
             log("iccTransmitApduBasicChannel: subId=" + subId + " cla=" + cla + " cmd="
                     + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
@@ -5782,6 +6101,10 @@
             String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduBasicChannelBySlot");
+
         if (DBG) {
             log("iccTransmitApduBasicChannelByPort: slotIndex=" + slotIndex + " portIndex="
                     + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
@@ -5834,6 +6157,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccExchangeSimIO");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccExchangeSimIO");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -5879,6 +6205,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getForbiddenPlmns");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (appType != TelephonyManager.APPTYPE_USIM
@@ -5915,6 +6244,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setForbiddenPlmns");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setForbiddenPlmns");
+
         if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
             loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
             throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
@@ -5944,6 +6276,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "sendEnvelopeWithStatus");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "sendEnvelopeWithStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             IccIoResult response = (IccIoResult) sendRequest(CMD_SEND_ENVELOPE, content, subId);
@@ -6049,6 +6384,9 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, phone.getSubId(), "resetModemConfig");
 
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "resetModemConfig");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 Boolean success = (Boolean) sendRequest(CMD_RESET_MODEM_CONFIG, null);
@@ -6075,6 +6413,9 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, phone.getSubId(), "rebootModem");
 
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "rebootModem");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 Boolean success = (Boolean) sendRequest(CMD_MODEM_REBOOT, null);
@@ -6095,6 +6436,9 @@
     public void resetIms(int slotIndex) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "resetIms");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mImsResolver == null) {
@@ -6374,6 +6718,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeAutomatic");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeAutomatic");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6404,6 +6751,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeManual");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeManual");
+
         final long identity = Binder.clearCallingIdentity();
         if (!isActiveSubscription(subId)) {
             return false;
@@ -6434,6 +6784,9 @@
                 .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getManualNetworkSelectionPlmn");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getManualNetworkSelectionPlmn");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6496,6 +6849,10 @@
     public void getCallForwarding(int subId, int callForwardingReason,
             ICallForwardingInfoCallback callback) {
         enforceReadPrivilegedPermission("getCallForwarding");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallForwarding");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -6548,6 +6905,10 @@
     public void setCallForwarding(int subId, CallForwardingInfo callForwardingInfo,
             IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallForwarding");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -6581,6 +6942,10 @@
     @Override
     public void getCallWaitingStatus(int subId, IIntegerConsumer callback) {
         enforceReadPrivilegedPermission("getCallWaitingStatus");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallWaitingStatus");
+
         long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -6632,6 +6997,10 @@
     @Override
     public void setCallWaitingStatus(int subId, boolean enable, IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallWaitingStatus");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("setCallWaitingStatus: subId " + subId + " enable: " + enable);
@@ -6728,6 +7097,10 @@
                 }
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestNetworkScan");
+
         int callingUid = Binder.getCallingUid();
         int callingPid = Binder.getCallingPid();
         final long identity = Binder.clearCallingIdentity();
@@ -6803,6 +7176,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getAllowedNetworkTypesBitmask");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllowedNetworkTypesBitmask");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("getAllowedNetworkTypesBitmask");
@@ -6827,6 +7203,10 @@
             @TelephonyManager.AllowedNetworkTypesReason int reason) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getAllowedNetworkTypesForReason");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllowedNetworkTypesForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).getAllowedNetworkTypes(reason);
@@ -6920,6 +7300,10 @@
                     "setAllowedNetworkTypesForReason cannot be called with carrier privileges for"
                             + " reason " + reason);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setAllowedNetworkTypesForReason");
+
         if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
             loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason);
             return false;
@@ -6965,6 +7349,10 @@
     @Override
     public boolean isTetheringApnRequiredForSubscriber(int subId) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isTetheringApnRequiredForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         final Phone phone = getPhone(subId);
         try {
@@ -7067,6 +7455,9 @@
             enforceReadPrivilegedPermission(functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -7113,6 +7504,8 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabledForReason");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -7141,6 +7534,9 @@
 
     @Override
     public int getCarrierPrivilegeStatus(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierPrivilegeStatus");
+
         // No permission needed; this only lets the caller inspect their own status.
         return getCarrierPrivilegeStatusForUidWithPermission(subId, Binder.getCallingUid());
     }
@@ -7148,6 +7544,10 @@
     @Override
     public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
         enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierPrivilegeStatusForUid");
+
         return getCarrierPrivilegeStatusForUidWithPermission(subId, uid);
     }
 
@@ -7168,6 +7568,10 @@
     @Override
     public int checkCarrierPrivilegesForPackage(int subId, String pkgName) {
         enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackage");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "checkCarrierPrivilegesForPackage");
+
         if (TextUtils.isEmpty(pkgName)) {
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
         }
@@ -7187,6 +7591,11 @@
     @Override
     public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
         enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackageAnyPhone");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "checkCarrierPrivilegesForPackageAnyPhone");
+
         return checkCarrierPrivilegesForPackageAnyPhoneWithPermission(pkgName);
     }
 
@@ -7215,6 +7624,11 @@
     @Override
     public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
         enforceReadPrivilegedPermission("getCarrierPackageNamesForIntentAndPhone");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getCarrierPackageNamesForIntentAndPhone");
+
         Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone == null) {
             return Collections.emptyList();
@@ -7243,6 +7657,11 @@
     @Override
     public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
         enforceReadPrivilegedPermission("getPackagesWithCarrierPrivilegesForAllPhones");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getPackagesWithCarrierPrivilegesForAllPhones");
+
         Set<String> privilegedPackages = new ArraySet<>();
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -7259,6 +7678,10 @@
     public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
         enforceReadPrivilegedPermission("getCarrierServicePackageNameForLogicalSlot");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getCarrierServicePackageNameForLogicalSlot");
+
         final Phone phone = PhoneFactory.getPhone(logicalSlotIndex);
         if (phone == null) {
             return null;
@@ -7287,6 +7710,9 @@
     public void setCallComposerStatus(int subId, int status) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallComposerStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -7310,6 +7736,9 @@
     public int getCallComposerStatus(int subId) {
         enforceReadPrivilegedPermission("getCallComposerStatus");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallComposerStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -7332,6 +7761,10 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setLine1NumberForDisplayForSubscriber");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "setLine1NumberForDisplayForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final String iccId = getIccId(subId);
@@ -7388,6 +7821,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getLine1NumberForDisplay");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             String iccId = getIccId(subId);
@@ -7515,6 +7951,9 @@
     public String[] getMergedImsisFromGroup(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getMergedImsisFromGroup");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getMergedImsisFromGroup");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final TelephonyManager telephonyManager = mApp.getSystemService(
@@ -7560,6 +7999,9 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setOperatorBrandOverride");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setOperatorBrandOverride");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -7606,6 +8048,9 @@
             throw e;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioAccessFamily");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
@@ -7626,6 +8071,10 @@
         } catch (PackageManager.NameNotFoundException e) {
             throw new SecurityException("Invalid package:" + callingPackage);
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "uploadCallComposerPicture");
+
         RoleManager rm = mApp.getSystemService(RoleManager.class);
         List<String> dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER);
         if (!dialerRoleHolders.contains(callingPackage)) {
@@ -7712,6 +8161,9 @@
         final Phone defaultPhone = getDefaultPhone();
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "enableVideoCalling");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsManager.getInstance(defaultPhone.getContext(),
@@ -7729,6 +8181,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_IMS, "isVideoCallingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // Check the user preference and the  system-level IMS setting. Even if the user has
@@ -7754,6 +8209,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "canChangeDtmfToneLength");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7772,6 +8230,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isWorldPhone");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7791,6 +8252,9 @@
 
     @Override
     public boolean isHearingAidCompatibilitySupported() {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isHearingAidCompatibilitySupported");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mApp.getResources().getBoolean(R.bool.hac_enabled);
@@ -7807,6 +8271,9 @@
      */
     @Override
     public boolean isRttSupported(int subscriptionId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "isRttSupported");
+
         final long identity = Binder.clearCallingIdentity();
         final Phone phone = getPhone(subscriptionId);
         if (phone == null) {
@@ -7816,8 +8283,9 @@
         try {
             boolean isCarrierSupported = mApp.getCarrierConfigForSubId(subscriptionId).getBoolean(
                     CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL);
-            boolean isDeviceSupported =
-                    phone.getContext().getResources().getBoolean(R.bool.config_support_rtt);
+            boolean isDeviceSupported = (phone.getContext().getResources() != null)
+                    ? phone.getContext().getResources().getBoolean(R.bool.config_support_rtt)
+                    : false;
             return isCarrierSupported && isDeviceSupported;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -7832,6 +8300,12 @@
     public boolean isRttEnabled(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
+                if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+                    return false;
+                }
+            }
+
             boolean isRttSupported = isRttSupported(subscriptionId);
             boolean isUserRttSettingOn = Settings.Secure.getInt(
                     mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
@@ -7921,6 +8395,10 @@
                         subscriptionId,
                         "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: "
                                 + subscriptionId);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getPhoneAccountHandleForSubscriptionId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -7988,6 +8466,10 @@
     @Override
     public void factoryReset(int subId, String callingPackage) {
         enforceSettingsPermission();
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "factoryReset");
+
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
         }
@@ -8060,6 +8542,10 @@
     @Override
     public String getSimLocaleForSubscriber(int subId) {
         enforceReadPrivilegedPermission("getSimLocaleForSubscriber, subId: " + subId);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSimLocaleForSubscriber");
+
         final Phone phone = getPhone(subId);
         if (phone == null) {
             log("getSimLocaleForSubscriber, invalid subId");
@@ -8142,6 +8628,10 @@
     @Override
     public void requestModemActivityInfo(ResultReceiver result) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "requestModemActivityInfo");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8286,6 +8776,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getServiceStateForSubscriber");
+
         boolean hasFinePermission = false;
         boolean hasCoarsePermission = false;
         if (!renounceFineLocationAccess) {
@@ -8365,6 +8858,9 @@
      */
     @Override
     public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoicemailRingtoneUri");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle);
@@ -8401,6 +8897,9 @@
                     "setVoicemailRingtoneUri");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailRingtoneUri");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8422,6 +8921,9 @@
      */
     @Override
     public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isVoicemailVibrationEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle);
@@ -8458,6 +8960,9 @@
                     "setVoicemailVibrationEnabled");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailVibrationEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8534,6 +9039,10 @@
     @Override
     public String getAidForAppType(int subId, int appType) {
         enforceReadPrivilegedPermission("getAidForAppType");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getAidForAppType");
+
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8591,6 +9100,10 @@
     @Override
     public String getCdmaPrlVersion(int subId) {
         enforceReadPrivilegedPermission("getCdmaPrlVersion");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaPrlVersion");
+
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8640,6 +9153,10 @@
     @TelephonyManager.SetCarrierRestrictionResult
     public int setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         if (carrierRestrictionRules == null) {
@@ -8666,6 +9183,10 @@
     @Override
     public CarrierRestrictionRules getAllowedCarriers() {
         enforceReadPrivilegedPermission("getAllowedCarriers");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "getAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8696,6 +9217,10 @@
     @Override
     public void getCarrierRestrictionStatus(IIntegerConsumer callback, String packageName) {
         enforceReadPermission("getCarrierRestrictionStatus");
+
+        enforceTelephonyFeatureWithException(packageName,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierRestrictionStatus");
+
         int carrierId = validateCallerAndGetCarrierId(packageName);
         if (carrierId == CarrierAllowListInfo.INVALID_CARRIER_ID) {
             Rlog.e(LOG_TAG, "getCarrierRestrictionStatus: caller is not registered");
@@ -8818,6 +9343,11 @@
     @Override
     public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "carrierActionReportDefaultNetworkStatus");
+
         final Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8842,6 +9372,10 @@
     @Override
     public void carrierActionResetAll(int subId) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "carrierActionResetAll");
+
         final Phone phone = getPhone(subId);
         if (phone == null) {
             loge("carrierAction: ResetAll fails with invalid sibId: " + subId);
@@ -8910,6 +9444,9 @@
                     callingPackage, null, null);
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataEnabledForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -8980,6 +9517,10 @@
     @Override
     public void setSimPowerStateForSlot(int slotIndex, int state) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setSimPowerStateForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
 
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -9009,6 +9550,11 @@
     public void setSimPowerStateForSlotWithCallback(int slotIndex, int state,
             IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "setSimPowerStateForSlotWithCallback");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
 
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -9046,6 +9592,10 @@
     @Override
     public boolean getEmergencyCallbackMode(int subId) {
         enforceReadPrivilegedPermission("getEmergencyCallbackMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyCallbackMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).isInEcm();
@@ -9063,6 +9613,9 @@
      */
     @Override
     public SignalStrength getSignalStrength(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getSignalStrength");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone p = getPhone(subId);
@@ -9092,6 +9645,9 @@
                 return TelephonyManager.RADIO_POWER_UNAVAILABLE;
             }
 
+            enforceTelephonyFeatureWithException(callingPackage,
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioPowerState");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 return phone.getRadioPowerState();
@@ -9132,6 +9688,9 @@
                     mApp, subId, functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataRoamingEnabled");
+
         boolean isEnabled = false;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9159,6 +9718,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setDataRoamingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataRoamingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -9176,6 +9738,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isManualNetworkSelectionAllowed");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "isManualNetworkSelectionAllowed");
+
         boolean isAllowed = true;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9222,6 +9787,10 @@
                 throw new SecurityException("Caller does not have permission.");
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getUiccCardsInfo");
+
         // checking compatibility, if calling app's target SDK is T and beyond.
         if (CompatChanges.isChangeEnabled(GET_API_SIGNATURES_FROM_UICC_PORT_INFO,
                 Binder.getCallingUid())) {
@@ -9332,6 +9901,9 @@
         // we are reading iccId which is PII data.
         enforceReadPrivilegedPermission("getUiccSlotsInfo");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getUiccSlotsInfo");
+
         // checking compatibility, if calling app's target SDK is T and beyond.
         if (CompatChanges.isChangeEnabled(GET_API_SIGNATURES_FROM_UICC_PORT_INFO,
                 Binder.getCallingUid())) {
@@ -9434,6 +10006,9 @@
     public boolean switchSlots(int[] physicalSlots) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "switchSlots");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             List<UiccSlotMapping> slotMappings = new ArrayList<>();
@@ -9453,6 +10028,9 @@
     public boolean setSimSlotMapping(@NonNull List<UiccSlotMapping> slotMapping) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setSimSlotMapping");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (Boolean) sendRequest(CMD_SWITCH_SLOTS, slotMapping);
@@ -9463,6 +10041,9 @@
 
     @Override
     public int getCardIdForDefaultEuicc(int subId, String callingPackage) {
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_EUICC, "getCardIdForDefaultEuicc");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return UiccController.getInstance().getCardIdForDefaultEuicc();
@@ -9629,6 +10210,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaRoamingMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaRoamingMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_GET_CDMA_ROAMING_MODE, null /* argument */, subId);
@@ -9642,6 +10226,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setCdmaRoamingMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "setCdmaRoamingMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (boolean) sendRequest(CMD_SET_CDMA_ROAMING_MODE, mode, subId);
@@ -9656,6 +10243,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaSubscriptionMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaSubscriptionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId);
@@ -9669,6 +10259,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setCdmaSubscriptionMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "setCdmaSubscriptionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (boolean) sendRequest(CMD_SET_CDMA_SUBSCRIPTION_MODE, mode, subId);
@@ -9685,6 +10278,10 @@
                 "getEmergencyNumberList")) {
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyNumberList");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Map<Integer, List<EmergencyNumber>> emergencyNumberListInternal = new HashMap<>();
@@ -9710,6 +10307,10 @@
                     .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, defaultPhone.getSubId(), "isEmergencyNumber(Potential)");
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isEmergencyNumber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9802,6 +10403,9 @@
     public int getEmergencyNumberDbVersion(int subId) {
         enforceReadPrivilegedPermission("getEmergencyNumberDbVersion");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyNumberDbVersion");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -9819,6 +10423,9 @@
     public void notifyOtaEmergencyNumberDbInstalled() {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "notifyOtaEmergencyNumberDbInstalled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9836,6 +10443,9 @@
     public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) {
         enforceActiveEmergencySessionPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "updateOtaEmergencyNumberDbFilePath");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9853,6 +10463,9 @@
     public void resetOtaEmergencyNumberDbFilePath() {
         enforceActiveEmergencySessionPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "resetOtaEmergencyNumberDbFilePath");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9893,6 +10506,9 @@
     public boolean enableModemForSlot(int slotIndex, boolean enable) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "enableModemForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -9921,6 +10537,9 @@
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isModemEnabledForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             try {
@@ -9937,6 +10556,9 @@
     public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setMultiSimCarrierRestriction");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mTelephonySharedPreferences.edit()
@@ -9956,6 +10578,9 @@
             return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isMultiSimSupported");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return isMultiSimSupportedInternal();
@@ -10007,6 +10632,10 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, "switchMultiSimConfig");
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "switchMultiSimConfig");
+
         final long identity = Binder.clearCallingIdentity();
 
         try {
@@ -10024,6 +10653,10 @@
     @Override
     public boolean isApplicationOnUicc(int subId, int appType) {
         enforceReadPrivilegedPermission("isApplicationOnUicc");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isApplicationOnUicc");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return false;
@@ -10060,6 +10693,11 @@
                 "doesSwitchMultiSimConfigTriggerReboot")) {
             return false;
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "doesSwitchMultiSimConfigTriggerReboot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.isRebootRequiredForModemConfigChange();
@@ -10080,6 +10718,10 @@
         // Verify that the callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
                 .checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSlotsMapping");
+
         final long identity = Binder.clearCallingIdentity();
         List<UiccSlotMapping> slotMap = new ArrayList<>();
         try {
@@ -10150,6 +10792,9 @@
         enforceReadPrivilegedPermission("Needs READ_PRIVILEGED_PHONE_STATE for "
                 + "isDataEnabledForApn");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabledForApn");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10172,6 +10817,9 @@
     public boolean isApnMetered(@ApnType int apnType, int subId) {
         enforceReadPrivilegedPermission("isApnMetered");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isApnMetered");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10189,6 +10837,10 @@
     public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
             int subscriptionId, IBooleanConsumer resultCallback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setSystemSelectionChannels");
+
         long token = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10223,6 +10875,10 @@
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getSystemSelectionChannels");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getSystemSelectionChannels");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10241,6 +10897,10 @@
     @Override
     public boolean isMvnoMatched(int slotIndex, int mvnoType, @NonNull String mvnoMatchData) {
         enforceReadPrivilegedPermission("isMvnoMatched");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isMvnoMatched");
+
         return UiccController.getInstance().mvnoMatches(slotIndex, mvnoType, mvnoMatchData);
     }
 
@@ -10288,6 +10948,9 @@
 
     @Override
     public String getMmsUAProfUrl(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getMmsUAProfUrl");
+
         //TODO investigate if this API should require proper permission check in R b/133791609
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10305,6 +10968,9 @@
 
     @Override
     public String getMmsUserAgent(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getMmsUserAgent");
+
         //TODO investigate if this API should require proper permission check in R b/133791609
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10324,6 +10990,9 @@
     public boolean isMobileDataPolicyEnabled(int subscriptionId, int policy) {
         enforceReadPrivilegedPermission("isMobileDataPolicyEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isMobileDataPolicyEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10340,6 +11009,9 @@
             boolean enabled) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setMobileDataPolicyEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10394,10 +11066,19 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // ProvisioningManager can not handle ServiceSpecificException.
-            // Throw the IllegalStateException and annotate ProvisioningManager.
-            throw new IllegalStateException("IMS not available on device.");
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // ProvisioningManager can not handle ServiceSpecificException.
+                // Throw the IllegalStateException and annotate ProvisioningManager.
+                throw new IllegalStateException("IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION,
+                    "notifyRcsAutoConfigurationReceived");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10412,6 +11093,9 @@
     public boolean isIccLockEnabled(int subId) {
         enforceReadPrivilegedPermission("isIccLockEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isIccLockEnabled");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10441,6 +11125,9 @@
     public int setIccLockEnabled(int subId, boolean enabled, String password) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setIccLockEnabled");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return 0;
@@ -10473,6 +11160,9 @@
     public int changeIccLockPassword(int subId, String oldPassword, String newPassword) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "changeIccLockPassword");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return 0;
@@ -10543,6 +11233,9 @@
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getEquivalentHomePlmns");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             throw new RuntimeException("phone is not available");
@@ -10559,6 +11252,10 @@
     @Override
     public boolean isRadioInterfaceCapabilitySupported(
             final @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "isRadioInterfaceCapabilitySupported");
+
         Set<String> radioInterfaceCapabilities =
                 mRadioInterfaceCapabilities.getCapabilities();
         if (radioInterfaceCapabilities == null) {
@@ -10575,6 +11272,10 @@
                 Binder.getCallingUid(), "bootstrapAuthenticationRequest",
                 Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
                 Manifest.permission.MODIFY_PHONE_STATE);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "bootstrapAuthenticationRequest");
+
         if (DBG) {
             log("bootstrapAuthenticationRequest, subId:" + subId + ", appType:"
                     + appType + ", NAF:" + nafUrl + ", sp:" + securityProtocol
@@ -10734,6 +11435,10 @@
         enforceModifyPermission();
 
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "sendThermalMitigationRequest");
+
         if (!getThermalMitigationAllowlist(getDefaultPhone().getContext())
                 .contains(callingPackage)) {
             throw new SecurityException("Calling package must be configured in the device config. "
@@ -10964,9 +11669,16 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "registerRcsProvisioningCallback");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10995,10 +11707,19 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // operation failed silently
-            Rlog.w(LOG_TAG, "IMS not available on device.");
-            return;
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // operation failed silently
+                Rlog.w(LOG_TAG, "IMS not available on device.");
+                return;
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION,
+                    "unregisterRcsProvisioningCallback");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -11021,10 +11742,17 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // ProvisioningManager can not handle ServiceSpecificException.
-            // Throw the IllegalStateException and annotate ProvisioningManager.
-            throw new IllegalStateException("IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // ProvisioningManager can not handle ServiceSpecificException.
+                // Throw the IllegalStateException and annotate ProvisioningManager.
+                throw new IllegalStateException("IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "triggerRcsReconfiguration");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -11046,9 +11774,16 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "setRcsClientConfiguration");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -11462,6 +12197,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setSignalStrengthUpdateRequest");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setSignalStrengthUpdateRequest");
+
         final int callingUid = Binder.getCallingUid();
         // Verify that tha callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
@@ -11488,6 +12226,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "clearSignalStrengthUpdateRequest");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "clearSignalStrengthUpdateRequest");
+
         final int callingUid = Binder.getCallingUid();
         // Verify that tha callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
@@ -11554,6 +12295,10 @@
     @Override
     public PhoneCapability getPhoneCapability() {
         enforceReadPrivilegedPermission("getPhoneCapability");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "getPhoneCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.getCurrentPhoneCapability();
@@ -11572,6 +12317,9 @@
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         enforceRebootPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "prepareForUnattendedReboot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null, workSource);
@@ -11592,6 +12340,10 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID, "getSlicingConfig");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "getSlicingConfig");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getDefaultPhone();
@@ -11620,6 +12372,9 @@
                     + "permission READ_BASIC_PHONE_STATE.");
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isPremiumCapabilityAvailableForPurchase");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             loge("isPremiumCapabilityAvailableForPurchase: phone is null, subId=" + subId);
@@ -11662,6 +12417,9 @@
             throw new SecurityException("purchasePremiumCapability requires permission INTERNET.");
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "purchasePremiumCapability");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             try {
@@ -11942,6 +12700,10 @@
             boolean updateIfNeeded) {
         enforceInteractAcrossUsersPermission("getDefaultRespondViaMessageApplication");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING,
+                "getDefaultRespondViaMessageApplication");
+
         Context context = getPhoneFromSubIdOrDefault(subId).getContext();
 
         UserHandle userHandle = null;
@@ -12031,6 +12793,9 @@
     @Override
     @SimState
     public int getSimStateForSlotIndex(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSimStateForSlotIndex");
+
         IccCardConstants.State simState;
         if (slotIndex < 0) {
             simState = IccCardConstants.State.UNKNOWN;
@@ -12106,6 +12871,10 @@
     public List<CellBroadcastIdRange> getCellBroadcastIdRanges(int subId) {
         mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
                 "getCellBroadcastIdRanges");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getCellBroadcastIdRanges");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhone(subId).getCellBroadcastIdRanges();
@@ -12125,6 +12894,10 @@
             @Nullable IIntegerConsumer callback) {
         mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
                 "setCellBroadcastIdRanges");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "setCellBroadcastIdRanges");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhoneFromSubId(subId);
@@ -12951,4 +13724,47 @@
             return mCarrierId;
         }
     }
+
+    /*
+    * PhoneInterfaceManager is a singleton. Unit test calls the init() with context.
+    * But the context that is passed in is unused if the phone app is already alive.
+    * In this case PackageManager object is different in PhoneInterfaceManager and Unit test.
+    */
+    @VisibleForTesting
+    public void setPackageManager(PackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    /*
+     * PhoneInterfaceManager is a singleton. Unit test calls the init() with FeatureFlags.
+     * But the FeatureFlags that is passed in is unused if the phone app is already alive.
+     * In this case FeatureFlags object is different in PhoneInterfaceManager and Unit test.
+     */
+    @VisibleForTesting
+    public void setFeatureFlags(FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String telephonyFeature, @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())) {
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(telephonyFeature)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + telephonyFeature);
+        }
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index 3015f76..a0304f6 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -50,6 +50,7 @@
         "testables",
         "platform-compat-test-rules",
         "flag-junit",
+        "telephony_flags_core_java_lib",
         "satellite-s2storage-rw",
         "satellite-s2storage-testutils",
         "s2-geometry-library-java",
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
index bd2e4f7..f4197d9 100644
--- a/tests/src/com/android/phone/CarrierConfigLoaderTest.java
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -30,6 +31,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
@@ -56,12 +58,17 @@
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -76,10 +83,14 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class CarrierConfigLoaderTest extends TelephonyTestBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
+    private static final String TAG = CarrierConfigLoaderTest.class.getSimpleName();
     private static final int DEFAULT_PHONE_ID = 0;
     private static final int DEFAULT_SUB_ID = SubscriptionManager.getDefaultSubscriptionId();
     private static final String PLATFORM_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
+    private static final String PLATFORM_CARRIER_CONFIG_FEATURE = "com.android.carrierconfig";
     private static final long PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE = 1;
     private static final String CARRIER_CONFIG_EXAMPLE_KEY =
             CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT;
@@ -92,6 +103,7 @@
     @Mock SubscriptionManagerService mSubscriptionManagerService;
     @Mock SharedPreferences mSharedPreferences;
     @Mock TelephonyRegistryManager mTelephonyRegistryManager;
+    @Mock FeatureFlags mFeatureFlags;
 
     private TelephonyManager mTelephonyManager;
     private CarrierConfigLoader mCarrierConfigLoader;
@@ -118,6 +130,8 @@
         doReturn(Build.FINGERPRINT).when(mSharedPreferences).getString(eq("build_fingerprint"),
                 any());
         doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(new String[]{TAG}).when(mPackageManager).getPackagesForUid(anyInt());
+
         doReturn(mResources).when(mContext).getResources();
         doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
                 mContext).getFilesDir();
@@ -141,7 +155,8 @@
         mHandlerThread.start();
 
         mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
-        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper());
+        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper(),
+                mFeatureFlags);
         mHandler = mCarrierConfigLoader.getHandler();
 
         // Clear all configs to have the same starting point.
@@ -412,6 +427,34 @@
         assertThat(dumpContent).doesNotContain("Permission Denial:");
     }
 
+    @Test
+    @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
+    public void testGetConfigForSubIdWithFeature_withTelephonyFeatureMapping() {
+        doNothing().when(mContext).enforcePermission(
+                eq(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE),
+                anyInt(), anyInt(), anyString());
+
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        doReturn(false).when(mPackageManager).hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
+
+        // Not defined required feature, expect UnsupportedOperationException
+        assertThrows(UnsupportedOperationException.class,
+                () -> mCarrierConfigLoader.getConfigForSubIdWithFeature(DEFAULT_SUB_ID,
+                        PLATFORM_CARRIER_CONFIG_PACKAGE, PLATFORM_CARRIER_CONFIG_FEATURE));
+
+        doReturn(true).when(mPackageManager).hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
+
+        // Defined required feature, not expect UnsupportedOperationException
+        try {
+            mCarrierConfigLoader.getConfigForSubIdWithFeature(DEFAULT_SUB_ID,
+                    PLATFORM_CARRIER_CONFIG_PACKAGE, PLATFORM_CARRIER_CONFIG_FEATURE);
+        } catch (UnsupportedOperationException e) {
+            fail("not expected UnsupportedOperationException");
+        }
+    }
+
     private static PersistableBundle getTestConfig() {
         PersistableBundle config = new PersistableBundle();
         config.putInt(CARRIER_CONFIG_EXAMPLE_KEY, CARRIER_CONFIG_EXAMPLE_VALUE);
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 150703d..a96bc2f 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
@@ -31,8 +32,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.permission.flags.Flags;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -49,9 +52,12 @@
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
@@ -62,9 +68,14 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class PhoneInterfaceManagerTest extends TelephonyTestBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private PhoneInterfaceManager mPhoneInterfaceManager;
     private SharedPreferences mSharedPreferences;
     private IIntegerConsumer mIIntegerConsumer;
+    private static final String sDebugPackageName =
+            PhoneInterfaceManagerTest.class.getPackageName();
 
     @Mock
     PhoneGlobals mPhoneGlobals;
@@ -72,7 +83,8 @@
     Phone mPhone;
     @Mock
     FeatureFlags mFeatureFlags;
-
+    @Mock
+    PackageManager mPackageManager;
     @Mock
     private SubscriptionManagerService mSubscriptionManagerService;
 
@@ -82,6 +94,8 @@
     @UiThreadTest
     public void setUp() throws Exception {
         super.setUp();
+        doReturn(sDebugPackageName).when(mPhoneGlobals).getOpPackageName();
+
         // Note that PhoneInterfaceManager is a singleton. Calling init gives us a handle to the
         // global singleton, but the context that is passed in is unused if the phone app is already
         // alive on a test devices. You must use the spy to mock behavior. Mocks stemming from the
@@ -93,6 +107,13 @@
         mSharedPreferences = mPhoneInterfaceManager.getSharedPreferences();
         mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED).commit();
         mIIntegerConsumer = mock(IIntegerConsumer.class);
+
+        // In order not to affect the existing implementation, define a telephony features
+        // and disabled enforce_telephony_feature_mapping_for_public_apis feature flag
+        mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
+        doReturn(false).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        mPhoneInterfaceManager.setPackageManager(mPackageManager);
+        doReturn(true).when(mPackageManager).hasSystemFeature(anyString());
     }
 
     @Test
@@ -359,4 +380,32 @@
         }
         assertEquals("Expected error to be empty, was " + error, error, "");
     }
+
+    @Test
+    @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
+    public void testTelephonyFeatureAndCompatChanges() {
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+        mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        mPhoneInterfaceManager.setPackageManager(mPackageManager);
+        doReturn(true).when(mPackageManager).hasSystemFeature(anyString());
+
+        // Enabled FeatureFlags and ENABLE_FEATURE_MAPPING, telephony features are defined
+        try {
+            // FEATURE_TELEPHONY_CALLING
+            mPhoneInterfaceManager.handlePinMmiForSubscriber(1, "123456789");
+
+            // FEATURE_TELEPHONY_RADIO_ACCESS
+            mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1);
+        } catch (Exception e) {
+            fail("Not expect exception " + e.getMessage());
+        }
+
+        // telephony features is not defined, expect UnsupportedOperationException.
+        doReturn(false).when(mPackageManager).hasSystemFeature(anyString());
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.handlePinMmiForSubscriber(1, "123456789"));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1));
+    }
 }