Enforce map the telephony features with APIs in PhoneInterfaceManager.

If the required telephony feature is not defined, throw UnsupportedOperationException.

Bug: 297989574
Test: atest PhoneInterfaceManagerTest
Change-Id: Ib449aa285d1d1df7dddc1d96c82ef7b07c243a1c
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index a968b82..3274b07 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()) {
@@ -4531,6 +4709,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);
@@ -4550,6 +4732,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);
@@ -4613,6 +4799,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAdvancedCallingSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4629,6 +4818,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);
@@ -4650,6 +4843,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);
@@ -4666,6 +4863,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);
@@ -4687,6 +4888,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);
@@ -4703,6 +4908,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);
@@ -4725,6 +4934,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);
@@ -4748,6 +4961,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);
@@ -4770,6 +4987,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);
@@ -4786,6 +5007,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);
@@ -4803,6 +5028,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);
@@ -4823,6 +5052,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);
@@ -4839,6 +5072,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);
@@ -4855,6 +5092,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);
@@ -4871,6 +5112,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);
@@ -4888,6 +5133,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);
@@ -4909,6 +5158,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);
@@ -5034,6 +5287,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5054,6 +5310,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5074,6 +5333,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5093,6 +5355,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5113,6 +5378,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5133,6 +5401,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isRcsProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5156,6 +5427,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.
@@ -5196,6 +5470,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.
@@ -5223,6 +5500,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.
@@ -5262,6 +5542,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.
@@ -5335,6 +5619,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);
@@ -5372,6 +5659,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getDataNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5400,6 +5690,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoiceNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5427,6 +5720,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);
@@ -5464,6 +5760,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);
@@ -5545,6 +5844,7 @@
     @Override
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(
             @NonNull IccLogicalChannelRequest request) {
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/ "iccOpenLogicalChannel");
 
@@ -5552,6 +5852,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);
     }
 
@@ -5598,6 +5901,9 @@
 
     @Override
     public boolean iccCloseLogicalChannel(@NonNull IccLogicalChannelRequest request) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccCloseLogicalChannel");
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/"iccCloseLogicalChannel");
 
@@ -5645,6 +5951,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="
@@ -5658,6 +5968,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="
@@ -5699,6 +6014,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);
@@ -5712,6 +6031,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="
@@ -5764,6 +6087,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccExchangeSimIO");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccExchangeSimIO");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -5809,6 +6135,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getForbiddenPlmns");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (appType != TelephonyManager.APPTYPE_USIM
@@ -5845,6 +6174,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");
@@ -5874,6 +6206,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);
@@ -5979,6 +6314,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);
@@ -6005,6 +6343,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);
@@ -6025,6 +6366,9 @@
     public void resetIms(int slotIndex) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "resetIms");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mImsResolver == null) {
@@ -6304,6 +6648,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeAutomatic");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeAutomatic");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6334,6 +6681,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeManual");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeManual");
+
         final long identity = Binder.clearCallingIdentity();
         if (!isActiveSubscription(subId)) {
             return false;
@@ -6364,6 +6714,9 @@
                 .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getManualNetworkSelectionPlmn");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getManualNetworkSelectionPlmn");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6426,6 +6779,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) {
@@ -6478,6 +6835,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) {
@@ -6511,6 +6872,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);
@@ -6562,6 +6927,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);
@@ -6658,6 +7027,10 @@
                 }
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestNetworkScan");
+
         int callingUid = Binder.getCallingUid();
         int callingPid = Binder.getCallingPid();
         final long identity = Binder.clearCallingIdentity();
@@ -6733,6 +7106,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getAllowedNetworkTypesBitmask");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllowedNetworkTypesBitmask");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("getAllowedNetworkTypesBitmask");
@@ -6757,6 +7133,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);
@@ -6850,6 +7230,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;
@@ -6895,6 +7279,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 {
@@ -6997,6 +7385,9 @@
             enforceReadPrivilegedPermission(functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -7043,6 +7434,8 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabledForReason");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -7071,6 +7464,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());
     }
@@ -7078,6 +7474,10 @@
     @Override
     public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
         enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierPrivilegeStatusForUid");
+
         return getCarrierPrivilegeStatusForUidWithPermission(subId, uid);
     }
 
@@ -7098,6 +7498,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;
         }
@@ -7117,6 +7521,11 @@
     @Override
     public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
         enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackageAnyPhone");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "checkCarrierPrivilegesForPackageAnyPhone");
+
         return checkCarrierPrivilegesForPackageAnyPhoneWithPermission(pkgName);
     }
 
@@ -7145,6 +7554,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();
@@ -7173,6 +7587,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 {
@@ -7189,6 +7608,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;
@@ -7217,6 +7640,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);
@@ -7240,6 +7666,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);
@@ -7262,6 +7691,10 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setLine1NumberForDisplayForSubscriber");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "setLine1NumberForDisplayForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final String iccId = getIccId(subId);
@@ -7318,6 +7751,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getLine1NumberForDisplay");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             String iccId = getIccId(subId);
@@ -7445,6 +7881,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(
@@ -7490,6 +7929,9 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setOperatorBrandOverride");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setOperatorBrandOverride");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -7536,6 +7978,9 @@
             throw e;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioAccessFamily");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
@@ -7556,6 +8001,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)) {
@@ -7642,6 +8091,9 @@
         final Phone defaultPhone = getDefaultPhone();
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "enableVideoCalling");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsManager.getInstance(defaultPhone.getContext(),
@@ -7659,6 +8111,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
@@ -7684,6 +8139,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "canChangeDtmfToneLength");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7702,6 +8160,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isWorldPhone");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7721,6 +8182,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);
@@ -7737,6 +8201,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) {
@@ -7746,8 +8213,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);
@@ -7762,6 +8230,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;
@@ -7851,6 +8325,10 @@
                         subscriptionId,
                         "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: "
                                 + subscriptionId);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getPhoneAccountHandleForSubscriptionId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -7918,6 +8396,10 @@
     @Override
     public void factoryReset(int subId, String callingPackage) {
         enforceSettingsPermission();
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "factoryReset");
+
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
         }
@@ -7990,6 +8472,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");
@@ -8072,6 +8558,10 @@
     @Override
     public void requestModemActivityInfo(ResultReceiver result) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "requestModemActivityInfo");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8216,6 +8706,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getServiceStateForSubscriber");
+
         boolean hasFinePermission = false;
         boolean hasCoarsePermission = false;
         if (!renounceFineLocationAccess) {
@@ -8295,6 +8788,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);
@@ -8331,6 +8827,9 @@
                     "setVoicemailRingtoneUri");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailRingtoneUri");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8352,6 +8851,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);
@@ -8388,6 +8890,9 @@
                     "setVoicemailVibrationEnabled");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailVibrationEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8464,6 +8969,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();
@@ -8521,6 +9030,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();
@@ -8570,6 +9083,10 @@
     @TelephonyManager.SetCarrierRestrictionResult
     public int setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         if (carrierRestrictionRules == null) {
@@ -8596,6 +9113,10 @@
     @Override
     public CarrierRestrictionRules getAllowedCarriers() {
         enforceReadPrivilegedPermission("getAllowedCarriers");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "getAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8626,6 +9147,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");
@@ -8748,6 +9273,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();
@@ -8772,6 +9302,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);
@@ -8840,6 +9374,9 @@
                     callingPackage, null, null);
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataEnabledForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -8910,6 +9447,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());
@@ -8939,6 +9480,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());
@@ -8976,6 +9522,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();
@@ -8993,6 +9543,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);
@@ -9022,6 +9575,9 @@
                 return TelephonyManager.RADIO_POWER_UNAVAILABLE;
             }
 
+            enforceTelephonyFeatureWithException(callingPackage,
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioPowerState");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 return phone.getRadioPowerState();
@@ -9062,6 +9618,9 @@
                     mApp, subId, functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataRoamingEnabled");
+
         boolean isEnabled = false;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9089,6 +9648,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setDataRoamingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataRoamingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -9106,6 +9668,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isManualNetworkSelectionAllowed");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "isManualNetworkSelectionAllowed");
+
         boolean isAllowed = true;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9152,6 +9717,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())) {
@@ -9262,6 +9831,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())) {
@@ -9364,6 +9936,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<>();
@@ -9383,6 +9958,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);
@@ -9393,6 +9971,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();
@@ -9559,6 +10140,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);
@@ -9572,6 +10156,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);
@@ -9586,6 +10173,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);
@@ -9599,6 +10189,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);
@@ -9615,6 +10208,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<>();
@@ -9640,6 +10237,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()) {
@@ -9732,6 +10333,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);
@@ -9749,6 +10353,9 @@
     public void notifyOtaEmergencyNumberDbInstalled() {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "notifyOtaEmergencyNumberDbInstalled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9766,6 +10373,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()) {
@@ -9783,6 +10393,9 @@
     public void resetOtaEmergencyNumberDbFilePath() {
         enforceActiveEmergencySessionPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "resetOtaEmergencyNumberDbFilePath");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9823,6 +10436,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);
@@ -9851,6 +10467,9 @@
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isModemEnabledForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             try {
@@ -9867,6 +10486,9 @@
     public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setMultiSimCarrierRestriction");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mTelephonySharedPreferences.edit()
@@ -9886,6 +10508,9 @@
             return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isMultiSimSupported");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return isMultiSimSupportedInternal();
@@ -9937,6 +10562,10 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, "switchMultiSimConfig");
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "switchMultiSimConfig");
+
         final long identity = Binder.clearCallingIdentity();
 
         try {
@@ -9954,6 +10583,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;
@@ -9990,6 +10623,11 @@
                 "doesSwitchMultiSimConfigTriggerReboot")) {
             return false;
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "doesSwitchMultiSimConfigTriggerReboot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.isRebootRequiredForModemConfigChange();
@@ -10010,6 +10648,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 {
@@ -10080,6 +10722,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 {
@@ -10102,6 +10747,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 {
@@ -10119,6 +10767,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);
@@ -10153,6 +10805,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 {
@@ -10171,6 +10827,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);
     }
 
@@ -10218,6 +10878,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 {
@@ -10235,6 +10898,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 {
@@ -10254,6 +10920,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);
@@ -10270,6 +10939,9 @@
             boolean enabled) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setMobileDataPolicyEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10324,10 +10996,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();
@@ -10342,6 +11023,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 {
@@ -10371,6 +11055,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;
@@ -10403,6 +11090,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;
@@ -10473,6 +11163,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");
@@ -10489,6 +11182,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) {
@@ -10505,6 +11202,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
@@ -10664,6 +11365,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. "
@@ -10894,9 +11599,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();
@@ -10925,10 +11637,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();
@@ -10951,10 +11672,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();
@@ -10976,9 +11704,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();
@@ -11392,6 +12127,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)
@@ -11418,6 +12156,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)
@@ -11484,6 +12225,10 @@
     @Override
     public PhoneCapability getPhoneCapability() {
         enforceReadPrivilegedPermission("getPhoneCapability");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "getPhoneCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.getCurrentPhoneCapability();
@@ -11502,6 +12247,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);
@@ -11522,6 +12270,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();
@@ -11550,6 +12302,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);
@@ -11592,6 +12347,9 @@
             throw new SecurityException("purchasePremiumCapability requires permission INTERNET.");
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "purchasePremiumCapability");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             try {
@@ -11872,6 +12630,10 @@
             boolean updateIfNeeded) {
         enforceInteractAcrossUsersPermission("getDefaultRespondViaMessageApplication");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING,
+                "getDefaultRespondViaMessageApplication");
+
         Context context = getPhoneFromSubIdOrDefault(subId).getContext();
 
         UserHandle userHandle = null;
@@ -11961,6 +12723,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;
@@ -12036,6 +12801,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();
@@ -12055,6 +12824,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);
@@ -12881,4 +13654,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);
+        }
+    }
 }