Merge "Update team owners." into main am: 4e7770f770 am: 722cb04bec am: 1a6b133783 am: e03fb103db

Original change: https://android-review.googlesource.com/c/platform/packages/services/Telephony/+/2716016

Change-Id: I7d0273cb96adf671b8f83bf7f1231c246d33ed1a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 124badf..59ff11d 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -207,6 +207,7 @@
     private static final int EVENT_QUERY_SMSC_DONE = 1005;
     private static final int EVENT_UPDATE_SMSC_DONE = 1006;
     private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 1007;
+    private static final int EVENT_UPDATE_NR_STATS = 1008;
 
     private static final int MENU_ITEM_VIEW_ADN            = 1;
     private static final int MENU_ITEM_VIEW_FDN            = 2;
@@ -379,7 +380,14 @@
             updateNetworkType();
             updateRawRegistrationState(serviceState);
             updateImsProvisionedState();
-            updateNrStats(serviceState);
+
+            // Since update NR stats includes a ril message to get slicing information, it runs
+            // as blocking during the timeout period of 1 second. if ServiceStateChanged event
+            // fires consecutively, RadioInfo can run for more than 10 seconds. This can cause ANR.
+            // Therefore, send event only when there is no same event being processed.
+            if (!mHandler.hasMessages(EVENT_UPDATE_NR_STATS)) {
+                mHandler.obtainMessage(EVENT_UPDATE_NR_STATS).sendToTarget();
+            }
         }
 
         @Override
@@ -465,6 +473,10 @@
                     }
                     updatePhysicalChannelConfiguration((List<PhysicalChannelConfig>) ar.result);
                     break;
+                case EVENT_UPDATE_NR_STATS:
+                    log("got EVENT_UPDATE_NR_STATS");
+                    updateNrStats();
+                    break;
                 default:
                     super.handleMessage(msg);
                     break;
@@ -690,7 +702,7 @@
         updateProperties();
         updateDnsCheckState();
         updateNetworkType();
-        updateNrStats(null);
+        updateNrStats();
 
         updateCellInfo(mCellInfoResult);
         updateSubscriptionIds();
@@ -1235,15 +1247,12 @@
                     AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
     }
 
-    private void updateNrStats(ServiceState serviceState) {
+    private void updateNrStats() {
         if ((mTelephonyManager.getSupportedRadioAccessFamily()
                 & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
             return;
         }
-        ServiceState ss = serviceState;
-        if (ss == null && mPhone != null) {
-            ss = mPhone.getServiceState();
-        }
+        ServiceState ss = (mPhone == null) ? null : mPhone.getServiceState();
         if (ss != null) {
             NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
@@ -1257,6 +1266,13 @@
             }
             mNrState.setText(NetworkRegistrationInfo.nrStateToString(ss.getNrState()));
             mNrFrequency.setText(ServiceState.frequencyRangeToString(ss.getNrFrequencyRange()));
+        } else {
+            Log.e(TAG, "Clear Nr stats by null service state");
+            mEndcAvailable.setText("");
+            mDcnrRestricted.setText("");
+            mNrAvailable.setText("");
+            mNrState.setText("");
+            mNrFrequency.setText("");
         }
 
         CompletableFuture<NetworkSlicingConfig> resultFuture = new CompletableFuture<>();
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
index d5ef816..3bfe1a4 100644
--- a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
@@ -46,6 +46,7 @@
     private static final String PROVISION_STATUS_KEY = "ProvStatus";
     private static final String SERVICE_FLOW_URL_KEY = "ServiceFlow_URL";
     private static final String SERVICE_FLOW_USERDATA_KEY = "ServiceFlow_UserData";
+    private static final String SERVICE_FLOW_CONTENTS_TYPE_KEY = "ServiceFlow_ContentsType";
     private static final String DEFAULT_EAP_AKA_RESPONSE = "Default EAP AKA response";
     /**
      * UUID to report an anomaly if an unexpected error is received during entitlement check.
@@ -120,13 +121,11 @@
         }
         try {
             JSONObject jsonAuthResponse = new JSONObject(response);
-            String entitlementStatus = null;
-            String provisionStatus = null;
             if (jsonAuthResponse.has(ServiceEntitlement.APP_DATA_PLAN_BOOST)) {
                 JSONObject jsonToken = jsonAuthResponse.getJSONObject(
                         ServiceEntitlement.APP_DATA_PLAN_BOOST);
                 if (jsonToken.has(ENTITLEMENT_STATUS_KEY)) {
-                    entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
+                    String entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
                     if (entitlementStatus == null) {
                         return null;
                     }
@@ -134,7 +133,7 @@
                             Integer.parseInt(entitlementStatus);
                 }
                 if (jsonToken.has(PROVISION_STATUS_KEY)) {
-                    provisionStatus = jsonToken.getString(PROVISION_STATUS_KEY);
+                    String provisionStatus = jsonToken.getString(PROVISION_STATUS_KEY);
                     if (provisionStatus != null) {
                         premiumNetworkEntitlementResponse.mProvisionStatus =
                                 Integer.parseInt(provisionStatus);
@@ -148,6 +147,10 @@
                     premiumNetworkEntitlementResponse.mServiceFlowUserData =
                             jsonToken.getString(SERVICE_FLOW_USERDATA_KEY);
                 }
+                if (jsonToken.has(SERVICE_FLOW_CONTENTS_TYPE_KEY)) {
+                    premiumNetworkEntitlementResponse.mServiceFlowContentsType =
+                            jsonToken.getString(SERVICE_FLOW_CONTENTS_TYPE_KEY);
+                }
             } else {
                 Log.e(TAG, "queryEntitlementStatus failed with no app");
             }
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
index ba44581..9126244 100644
--- a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
@@ -25,19 +25,19 @@
  *
  * The relationship between entitlement status (left column) and provision status (top row)
  * is defined in the table below:
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * |              | Not Provisioned |    Provisioned    |   Not Available   |  In Progress  |
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * |   Disabled   |   Check failed  |    Check failed   |    Check failed   |  Check failed |
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * |    Enabled   |  Carrier error  |  Display webview  |  Display webview  | Carrier error |
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * | Incompatible |   Check failed  |    Check failed   |    Check failed   |  Check failed |
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * | Provisioning |  Carrier error  |   Carrier error   |    In Progress    |  In Progress  |
- * +--------------+-----------------+-------------------+-------------------+---------------+
- * |   Included   |  Carrier error  | Already purchased | Already purchased | Carrier error |
- * +--------------+-----------------+-------------------+-------------------+---------------+
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * |              | Not Provisioned |    Provisioned    |   Not Available   |   In Progress   |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * |   Disabled   |   Check failed  |    Check failed   |    Check failed   |   Check failed  |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * |    Enabled   | Display webview | Already purchased | Already purchased |   In progress   |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * | Incompatible |   Check failed  |    Check failed   |    Check failed   |   Check failed  |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * | Provisioning |  Carrier error  |   Carrier error   |    In progress    |   In progress   |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
+ * |   Included   |  Carrier error  | Already purchased | Already purchased |  Carrier error  |
+ * +--------------+-----------------+-------------------+-------------------+-----------------+
  */
 public class PremiumNetworkEntitlementResponse {
     public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED = 0;
@@ -72,13 +72,19 @@
     @PremiumNetworkProvisionStatus public int mProvisionStatus;
     @NonNull public String mServiceFlowURL;
     @NonNull public String mServiceFlowUserData;
+    @NonNull public String mServiceFlowContentsType;
 
     /**
-     * @return {@code true} if the premium network is provisioned and {@code false} otherwise.
+     * @return {@code true} if the premium network is already purchased and {@code false} otherwise.
      */
-    public boolean isProvisioned() {
-        return !isInvalidResponse()
-                && mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED;
+    public boolean isAlreadyPurchased() {
+        switch (mEntitlementStatus) {
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED:
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED:
+                return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED
+                        || mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE;
+        }
+        return false;
     }
 
     /**
@@ -86,8 +92,14 @@
      *         {@code false} otherwise.
      */
     public boolean isProvisioningInProgress() {
-        return !isInvalidResponse()
-                && mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING;
+        switch (mEntitlementStatus) {
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED:
+                return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING:
+                return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+                        || mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_NOT_AVAILABLE;
+        }
+        return false;
     }
 
     /**
@@ -108,7 +120,6 @@
      */
     public boolean isInvalidResponse() {
         switch (mEntitlementStatus) {
-            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED:
             case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED:
                 return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED
                         || mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
@@ -123,6 +134,7 @@
     @NonNull public String toString() {
         return "PremiumNetworkEntitlementResponse{mEntitlementStatus=" + mEntitlementStatus
                 + ", mProvisionStatus=" + mProvisionStatus + ", mServiceFlowURL=" + mServiceFlowURL
-                + ", mServiceFlowUserData" + mServiceFlowUserData + "}";
+                + ", mServiceFlowUserData=" + mServiceFlowUserData + ", mServiceFlowContentsType="
+                + mServiceFlowContentsType + "}";
     }
 }
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
index b1abe56..4984b23 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -48,6 +48,9 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.NetworkSlicingConfig;
+import android.telephony.data.RouteSelectionDescriptor;
+import android.telephony.data.TrafficDescriptor;
+import android.telephony.data.UrspRule;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.URLUtil;
@@ -92,12 +95,15 @@
     public static final int FAILURE_CODE_UNKNOWN = 0;
     /** Performance boost purchase failed because the carrier URL is unavailable. */
     public static final int FAILURE_CODE_CARRIER_URL_UNAVAILABLE = 1;
-    /** Performance boost purchase failed because the server is unreachable. */
-    public static final int FAILURE_CODE_SERVER_UNREACHABLE = 2;
     /** Performance boost purchase failed because user authentication failed. */
-    public static final int FAILURE_CODE_AUTHENTICATION_FAILED = 3;
+    public static final int FAILURE_CODE_AUTHENTICATION_FAILED = 2;
     /** Performance boost purchase failed because the payment failed. */
-    public static final int FAILURE_CODE_PAYMENT_FAILED = 4;
+    public static final int FAILURE_CODE_PAYMENT_FAILED = 3;
+    /**
+     * Performance boost purchase failed because the content type was specified but
+     * user data does not exist.
+     */
+    public static final int FAILURE_CODE_NO_USER_DATA = 4;
 
     /**
      * Failure codes that the carrier website can return when a premium capability purchase fails.
@@ -106,9 +112,9 @@
     @IntDef(prefix = { "FAILURE_CODE_" }, value = {
             FAILURE_CODE_UNKNOWN,
             FAILURE_CODE_CARRIER_URL_UNAVAILABLE,
-            FAILURE_CODE_SERVER_UNREACHABLE,
             FAILURE_CODE_AUTHENTICATION_FAILED,
-            FAILURE_CODE_PAYMENT_FAILED})
+            FAILURE_CODE_PAYMENT_FAILED,
+            FAILURE_CODE_NO_USER_DATA})
     public @interface FailureCode {}
 
     /** Value for an invalid premium capability. */
@@ -177,6 +183,12 @@
     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION =
             "com.android.phone.slice.action."
                     + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION";
+    /**
+     * Action indicating the performance boost notification was not shown because the user
+     * disabled notifications for the application or channel.
+     */
+    private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED =
+            "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED";
     /** Action indicating the purchase request was successful. */
     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS =
             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS";
@@ -209,6 +221,8 @@
     public static final String EXTRA_CARRIER = "com.android.phone.slice.extra.CARRIER";
     /** Extra for the user data received from the entitlement service to send to the webapp. */
     public static final String EXTRA_USER_DATA = "com.android.phone.slice.extra.USER_DATA";
+    /** Extra for the contents type received from the entitlement service to send to the webapp. */
+    public static final String EXTRA_CONTENTS_TYPE = "com.android.phone.slice.extra.CONTENTS_TYPE";
     /**
      * Extra for the canceled PendingIntent that the slice purchase application can send as a
      * response if the performance boost notification or WebView was canceled by the user.
@@ -242,6 +256,14 @@
     public static final String EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION =
             "com.android.phone.slice.extra.INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION";
     /**
+     * Extra for the notifications disabled PendingIntent that the slice purchase application can
+     * send as a response if the premium capability purchase request failed because the user
+     * disabled notifications for the application or channel.
+     * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED}.
+     */
+    public static final String EXTRA_INTENT_NOTIFICATIONS_DISABLED =
+            "com.android.phone.slice.extra.INTENT_NOTIFICATIONS_DISABLED";
+    /**
      * Extra for the success PendingIntent that the slice purchase application can send as a
      * response if the premium capability purchase request was successful.
      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS}.
@@ -320,7 +342,7 @@
         /**
          * Create a SlicePurchaseControllerBroadcastReceiver for the given capability
          *
-         * @param capability The requested capability to listen to response for.
+         * @param capability The requested premium capability to listen to response for.
          */
         SlicePurchaseControllerBroadcastReceiver(
                 @TelephonyManager.PremiumCapability int capability) {
@@ -391,6 +413,16 @@
                             false);
                     break;
                 }
+                case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED: {
+                    logd("Slice purchase application unable to show notification for capability: "
+                            + TelephonyManager.convertPremiumCapabilityToString(capability)
+                            + " because the user has disabled notifications.");
+                    SlicePurchaseController.getInstance(phoneId)
+                            .handlePurchaseResult(capability,
+                            TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED,
+                            true);
+                    break;
+                }
                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS: {
                     long duration = intent.getLongExtra(EXTRA_PURCHASE_DURATION, 0);
                     SlicePurchaseController.getInstance(phoneId).onCarrierSuccess(
@@ -475,6 +507,17 @@
         mLocalDate = localDate;
     }
 
+    /**
+     * Set the NetworkSlicingConfig to use for determining whether the premium capability was
+     * successfully set up on the carrier network.
+     *
+     * @param slicingConfig The LocalDate instance to use.
+     */
+    @VisibleForTesting
+    public void setSlicingConfig(@NonNull NetworkSlicingConfig slicingConfig) {
+        mSlicingConfig = slicingConfig;
+    }
+
     @Override
     public void handleMessage(@NonNull Message msg) {
         switch (msg.what) {
@@ -488,7 +531,8 @@
             case EVENT_SLICING_CONFIG_CHANGED: {
                 AsyncResult ar = (AsyncResult) msg.obj;
                 NetworkSlicingConfig config = (NetworkSlicingConfig) ar.result;
-                logd("EVENT_SLICING_CONFIG_CHANGED: from " + mSlicingConfig + " to " + config);
+                logd("EVENT_SLICING_CONFIG_CHANGED: previous= " + mSlicingConfig);
+                logd("EVENT_SLICING_CONFIG_CHANGED: current= " + config);
                 mSlicingConfig = config;
                 onSlicingConfigChanged();
                 break;
@@ -690,6 +734,17 @@
 
     private void onStartSlicePurchaseApplication(
             @TelephonyManager.PremiumCapability int capability) {
+        updateNotificationCounts();
+        if (mMonthlyCount >= getCarrierConfigs().getInt(
+                CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT)
+                || mDailyCount >= getCarrierConfigs().getInt(
+                CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT)) {
+            logd("Reached maximum number of performance boost notifications.");
+            handlePurchaseResult(capability,
+                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, false);
+            return;
+        }
+
         final PremiumNetworkEntitlementApi premiumNetworkEntitlementApi =
                 getPremiumNetworkEntitlementApi();
         PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
@@ -711,8 +766,8 @@
             return;
         }
 
-        if (premiumNetworkEntitlementResponse.isProvisioned()) {
-            logd("Entitlement Check: Already provisioned.");
+        if (premiumNetworkEntitlementResponse.isAlreadyPurchased()) {
+            logd("Entitlement Check: Already purchased/provisioned.");
             handlePurchaseResult(capability,
                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED, true);
             return;
@@ -725,7 +780,6 @@
             return;
         }
 
-        String userData = premiumNetworkEntitlementResponse.mServiceFlowUserData;
         String purchaseUrl = getPurchaseUrl(premiumNetworkEntitlementResponse);
         String carrier = getSimOperator();
         if (TextUtils.isEmpty(purchaseUrl) || TextUtils.isEmpty(carrier)) {
@@ -734,17 +788,6 @@
             return;
         }
 
-        updateNotificationCounts();
-        if (mMonthlyCount >= getCarrierConfigs().getInt(
-                CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT)
-                || mDailyCount >= getCarrierConfigs().getInt(
-                CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT)) {
-            logd("Reached maximum number of performance boost notifications.");
-            handlePurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, false);
-            return;
-        }
-
         // Start timeout for purchase completion.
         long timeout = getCarrierConfigs().getLong(CarrierConfigManager
                 .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG);
@@ -761,9 +804,9 @@
         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
         intent.putExtra(EXTRA_PURCHASE_URL, purchaseUrl);
         intent.putExtra(EXTRA_CARRIER, carrier);
-        if (!TextUtils.isEmpty(userData)) {
-            intent.putExtra(EXTRA_USER_DATA, userData);
-        }
+        intent.putExtra(EXTRA_USER_DATA, premiumNetworkEntitlementResponse.mServiceFlowUserData);
+        intent.putExtra(EXTRA_CONTENTS_TYPE,
+                premiumNetworkEntitlementResponse.mServiceFlowContentsType);
         intent.putExtra(EXTRA_INTENT_CANCELED, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED, capability, false));
         intent.putExtra(EXTRA_INTENT_CARRIER_ERROR, createPendingIntent(
@@ -773,6 +816,8 @@
         intent.putExtra(EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION, capability,
                 false));
+        intent.putExtra(EXTRA_INTENT_NOTIFICATIONS_DISABLED, createPendingIntent(
+                ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED, capability, false));
         intent.putExtra(EXTRA_INTENT_SUCCESS, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS, capability, true));
         intent.putExtra(EXTRA_INTENT_NOTIFICATION_SHOWN, createPendingIntent(
@@ -788,6 +833,7 @@
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR);
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED);
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION);
+        filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED);
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS);
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN);
         mPhone.getContext().registerReceiver(
@@ -977,7 +1023,8 @@
 
     private long getThrottleDuration(@TelephonyManager.PurchasePremiumCapabilityResult int result) {
         if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
-                || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT) {
+                || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+                || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED) {
             return getCarrierConfigs().getLong(CarrierConfigManager
                     .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
         }
@@ -1037,26 +1084,71 @@
         return mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId();
     }
 
-    private boolean isSlicingConfigActive(@TelephonyManager.PremiumCapability int capability) {
+    /**
+     * Check whether the current network slicing configuration indicates that the given premium
+     * capability is active and set up on the carrier network.
+     * @param capability The premium capability to check for.
+     * @return {@code true} if the slicing config indicates the capability is active and
+     * {@code false} otherwise.
+     */
+    @VisibleForTesting
+    public boolean isSlicingConfigActive(@TelephonyManager.PremiumCapability int capability) {
         if (mSlicingConfig == null) {
             return false;
         }
-        int capabilityServiceType = getSliceServiceType(capability);
-        for (NetworkSliceInfo sliceInfo : mSlicingConfig.getSliceInfo()) {
-            if (sliceInfo.getSliceServiceType() == capabilityServiceType
-                    && sliceInfo.getStatus() == NetworkSliceInfo.SLICE_STATUS_ALLOWED) {
-                return true;
+        for (UrspRule urspRule : mSlicingConfig.getUrspRules()) {
+            for (TrafficDescriptor trafficDescriptor : urspRule.getTrafficDescriptors()) {
+                TrafficDescriptor.OsAppId osAppId =
+                        new TrafficDescriptor.OsAppId(trafficDescriptor.getOsAppId());
+                if (osAppId.getAppId().equals(getAppId(capability))) {
+                    for (RouteSelectionDescriptor rsd : urspRule.getRouteSelectionDescriptor()) {
+                        for (NetworkSliceInfo sliceInfo : rsd.getSliceInfo()) {
+                            if (sliceInfo.getStatus() == NetworkSliceInfo.SLICE_STATUS_ALLOWED
+                                    && getSliceServiceTypes(capability).contains(
+                                            sliceInfo.getSliceServiceType())) {
+                                return true;
+                            }
+                        }
+                    }
+                }
             }
         }
         return false;
     }
 
-    @NetworkSliceInfo.SliceServiceType private int getSliceServiceType(
-            @TelephonyManager.PremiumCapability int capability) {
+    /**
+     * Get the application ID associated with the given premium capability.
+     * The app ID is a field in {@link TrafficDescriptor} that helps match URSP rules to determine
+     * whether the premium capability was successfully set up on the carrier network.
+     * @param capability The premium capability to get the app ID for.
+     * @return The application ID associated with the premium capability.
+     */
+    @VisibleForTesting
+    @NonNull public static String getAppId(@TelephonyManager.PremiumCapability int capability) {
         if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
-            return NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC;
+            return "PRIORITIZE_LATENCY";
         }
-        return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+        return "";
+    }
+
+    /**
+     * Get the slice service types associated with the given premium capability.
+     * The slice service type is a field in {@link NetworkSliceInfo} that helps to match determine
+     * whether the premium capability was successfully set up on the carrier network.
+     * @param capability The premium capability to get the associated slice service types for.
+     * @return A set of slice service types associated with the premium capability.
+     */
+    @VisibleForTesting
+    @NonNull @NetworkSliceInfo.SliceServiceType public static Set<Integer> getSliceServiceTypes(
+            @TelephonyManager.PremiumCapability int capability) {
+        Set<Integer> sliceServiceTypes = new HashSet<>();
+        if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
+            sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_EMBB);
+            sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC);
+        } else {
+            sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE);
+        }
+        return sliceServiceTypes;
     }
 
     private boolean isNetworkAvailable() {
@@ -1094,9 +1186,9 @@
         switch (failureCode) {
             case FAILURE_CODE_UNKNOWN: return "UNKNOWN";
             case FAILURE_CODE_CARRIER_URL_UNAVAILABLE: return "CARRIER_URL_UNAVAILABLE";
-            case FAILURE_CODE_SERVER_UNREACHABLE: return "SERVER_UNREACHABLE";
             case FAILURE_CODE_AUTHENTICATION_FAILED: return "AUTHENTICATION_FAILED";
             case FAILURE_CODE_PAYMENT_FAILED: return "PAYMENT_FAILED";
+            case FAILURE_CODE_NO_USER_DATA: return "NO_USER_DATA";
             default:
                 return "UNKNOWN(" + failureCode + ")";
         }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 2b69b82..ea29b77 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -16,6 +16,7 @@
 
 package com.android.services.telephony;
 
+import android.app.ActivityManager;
 import android.app.PropertyInvalidatedCache;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -1191,7 +1192,7 @@
     private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
     private int mServiceState = ServiceState.STATE_POWER_OFF;
     private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private boolean mIsPrimaryUser = true;
+    private boolean mIsPrimaryUser = UserHandle.of(ActivityManager.getCurrentUser()).isSystem();
     private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
     private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
 
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 6d136b0..7aa0e7b 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -149,6 +149,8 @@
     private static final int MSG_DTMF_DONE = 22;
     private static final int MSG_MEDIA_ATTRIBUTES_CHANGED = 23;
     private static final int MSG_ON_RTT_INITIATED = 24;
+    private static final int MSG_HOLD = 25;
+    private static final int MSG_UNHOLD = 26;
 
     private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
@@ -344,6 +346,12 @@
                     }
                     sendRttInitiationSuccess();
                     break;
+                case MSG_HOLD:
+                    performHold();
+                    break;
+                case MSG_UNHOLD:
+                    performUnhold();
+                    break;
             }
         }
     };
@@ -1049,12 +1057,12 @@
 
     @Override
     public void onHold() {
-        performHold();
+        mHandler.obtainMessage(MSG_HOLD).sendToTarget();
     }
 
     @Override
     public void onUnhold() {
-        performUnhold();
+        mHandler.obtainMessage(MSG_UNHOLD).sendToTarget();
     }
 
     @Override
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index bf7ce00..93a38dd 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1171,7 +1171,8 @@
             }
 
             if (!isEmergencyNumber) {
-                if (mSatelliteController.isSatelliteEnabled()) {
+                if (mSatelliteController.isSatelliteEnabled()
+                        || isCallDisallowedDueToSatellite(phone)) {
                     Log.d(this, "onCreateOutgoingConnection, cannot make call in satellite mode.");
                     return Connection.createFailedConnection(
                             mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -4067,4 +4068,34 @@
         connection.addTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
         mSatelliteSOSMessageRecommender.onEmergencyCallStarted(connection, phone);
     }
+
+    /**
+     * Check whether making a call is disallowed while using satellite
+     * @param phone phone object whose supported services needs to be checked
+     * @return {@code true} if network does not support calls while using satellite
+     * else {@code false}.
+     */
+    private boolean isCallDisallowedDueToSatellite(Phone phone) {
+        if (phone == null) {
+            return false;
+        }
+
+        ServiceState serviceState = phone.getServiceState();
+        if (!serviceState.isUsingNonTerrestrialNetwork()) {
+            // Device is not connected to satellite
+            return false;
+        }
+
+        for (NetworkRegistrationInfo nri : serviceState.getNetworkRegistrationInfoList()) {
+            if (nri.isNonTerrestrialNetwork()
+                    && nri.getAvailableServices().contains(
+                    NetworkRegistrationInfo.SERVICE_TYPE_VOICE)) {
+                // Call is supported while using satellite
+                return false;
+            }
+        }
+
+        // Call is disallowed while using satellite
+        return true;
+    }
 }
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
index b2a4a9f..ca67d63 100644
--- a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -50,6 +51,9 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.NetworkSlicingConfig;
+import android.telephony.data.RouteSelectionDescriptor;
+import android.telephony.data.TrafficDescriptor;
+import android.telephony.data.UrspRule;
 import android.testing.TestableLooper;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -66,7 +70,9 @@
 import org.mockito.Mockito;
 
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 @RunWith(AndroidJUnit4.class)
@@ -578,7 +584,7 @@
         intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
         intent.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
-                SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE);
+                SlicePurchaseController.FAILURE_CODE_CARRIER_URL_UNAVAILABLE);
         mContext.getBroadcastReceiver().onReceive(mContext, intent);
         mTestableLooper.processAllMessages();
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, mResult);
@@ -619,6 +625,7 @@
     public void testPurchasePremiumCapabilityResultNotDefaultDataSubscriptionResponse() {
         sendValidPurchaseRequest();
 
+        // broadcast NOT_DEFAULT_DATA_SUBSCRIPTION response from slice purchase application
         Intent intent = new Intent();
         intent.setAction("com.android.phone.slice.action."
                 + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION");
@@ -636,6 +643,34 @@
     }
 
     @Test
+    public void testPurchasePremiumCapabilityResultNotificationsDisabled() {
+        sendValidPurchaseRequest();
+
+        // broadcast NOTIFICATIONS_DISABLED response from slice purchase application
+        Intent intent = new Intent();
+        intent.setAction("com.android.phone.slice.action."
+                + "SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        mContext.getBroadcastReceiver().onReceive(mContext, intent);
+        mTestableLooper.processAllMessages();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED, mResult);
+
+        // retry to verify throttled
+        mSlicePurchaseController.purchasePremiumCapability(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
+        mTestableLooper.processAllMessages();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+        // retry to verify unthrottled
+        mTestableLooper.moveTimeForward(THROTTLE_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
     public void testPurchasePremiumCapabilityResultNotificationThrottled() {
         mSlicePurchaseController.setLocalDate(LocalDate.of(YEAR, MONTH, DATE));
         mSlicePurchaseController.updateNotificationCounts();
@@ -670,6 +705,69 @@
         assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
     }
 
+    @Test
+    public void testIsSlicingConfigActive_emptyUrspRules() {
+        int capability = TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY;
+        NetworkSliceInfo sliceInfo = createNetworkSliceInfo(
+                getRandomSliceServiceType(capability), true);
+        NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig(
+                Collections.emptyList(), Collections.singletonList(sliceInfo));
+        mSlicePurchaseController.setSlicingConfig(slicingConfig);
+
+        assertFalse(mSlicePurchaseController.isSlicingConfigActive(capability));
+    }
+
+    @Test
+    public void testIsSlicingConfigActive_noMatchingTrafficDescriptor() {
+        int capability = TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY;
+        NetworkSliceInfo sliceInfo = createNetworkSliceInfo(
+                getRandomSliceServiceType(capability), true);
+        TrafficDescriptor trafficDescriptor = createTrafficDescriptor("ENTERPRISE");
+        RouteSelectionDescriptor routeSelectionDescriptor = createRouteSelectionDescriptor(
+                Collections.singletonList(sliceInfo));
+        NetworkSlicingConfig slicingConfig = createNetworkSlicingConfig(
+                Collections.singletonList(sliceInfo),
+                Collections.singletonList(trafficDescriptor),
+                Collections.singletonList(routeSelectionDescriptor));
+        mSlicePurchaseController.setSlicingConfig(slicingConfig);
+
+        assertFalse(mSlicePurchaseController.isSlicingConfigActive(capability));
+    }
+
+    @Test
+    public void testIsSlicingConfigActive_multipleElements() {
+        int capability = TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY;
+        NetworkSliceInfo sliceInfo1 = createNetworkSliceInfo(
+                getRandomSliceServiceType(SlicePurchaseController.PREMIUM_CAPABILITY_INVALID),
+                false);
+        NetworkSliceInfo sliceInfo2 = createNetworkSliceInfo(
+                getRandomSliceServiceType(capability), true);
+        List<NetworkSliceInfo> sliceInfos = new ArrayList<>();
+        sliceInfos.add(sliceInfo1);
+        sliceInfos.add(sliceInfo2);
+
+        TrafficDescriptor trafficDescriptor1 = createTrafficDescriptor("ENTERPRISE");
+        TrafficDescriptor trafficDescriptor2 = createTrafficDescriptor(
+                SlicePurchaseController.getAppId(capability));
+        List<TrafficDescriptor> trafficDescriptors = new ArrayList<>();
+        trafficDescriptors.add(trafficDescriptor1);
+        trafficDescriptors.add(trafficDescriptor2);
+
+        RouteSelectionDescriptor routeSelectionDescriptor1 = createRouteSelectionDescriptor(
+                Collections.emptyList());
+        RouteSelectionDescriptor routeSelectionDescriptor2 = createRouteSelectionDescriptor(
+                sliceInfos);
+        List<RouteSelectionDescriptor> routeSelectionDescriptors = new ArrayList<>();
+        routeSelectionDescriptors.add(routeSelectionDescriptor1);
+        routeSelectionDescriptors.add(routeSelectionDescriptor2);
+
+        NetworkSlicingConfig slicingConfig = createNetworkSlicingConfig(
+                sliceInfos, trafficDescriptors, routeSelectionDescriptors);
+        mSlicePurchaseController.setSlicingConfig(slicingConfig);
+
+        assertTrue(mSlicePurchaseController.isSlicingConfigActive(capability));
+    }
+
     private void completeSuccessfulPurchase() {
         sendValidPurchaseRequest();
 
@@ -726,7 +824,7 @@
         mEntitlementResponse.mEntitlementStatus =
                 PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED;
         mEntitlementResponse.mProvisionStatus =
-                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED;
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED;
 
         // send purchase request
         mSlicePurchaseController.purchasePremiumCapability(
@@ -744,18 +842,61 @@
     }
 
     private void sendNetworkSlicingConfig(int capability, boolean configActive) {
-        int sliceServiceType = capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY
-                ? NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC
-                : NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
-        NetworkSliceInfo sliceInfo = new NetworkSliceInfo.Builder()
-                .setStatus(configActive ? NetworkSliceInfo.SLICE_STATUS_ALLOWED
-                        : NetworkSliceInfo.SLICE_STATUS_UNKNOWN)
-                .setSliceServiceType(sliceServiceType)
-                .build();
-        NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig(Collections.emptyList(),
+        NetworkSliceInfo sliceInfo = createNetworkSliceInfo(
+                getRandomSliceServiceType(capability), configActive);
+        TrafficDescriptor trafficDescriptor = createTrafficDescriptor(
+                SlicePurchaseController.getAppId(capability));
+        RouteSelectionDescriptor routeSelectionDescriptor = createRouteSelectionDescriptor(
                 Collections.singletonList(sliceInfo));
+        NetworkSlicingConfig slicingConfig = createNetworkSlicingConfig(
+                Collections.singletonList(sliceInfo),
+                Collections.singletonList(trafficDescriptor),
+                Collections.singletonList(routeSelectionDescriptor));
         mSlicePurchaseController.obtainMessage(2 /* EVENT_SLICING_CONFIG_CHANGED */,
                 new AsyncResult(null, slicingConfig, null)).sendToTarget();
         mTestableLooper.processAllMessages();
     }
+
+    @NetworkSliceInfo.SliceServiceType private int getRandomSliceServiceType(
+            @TelephonyManager.PremiumCapability int capability) {
+        for (int sliceServiceType : SlicePurchaseController.getSliceServiceTypes(capability)) {
+            // Get a random valid sst from the set
+            return sliceServiceType;
+        }
+        return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+    }
+
+    @NonNull private NetworkSliceInfo createNetworkSliceInfo(
+            @NetworkSliceInfo.SliceServiceType int sliceServiceType, boolean active) {
+        return new NetworkSliceInfo.Builder()
+                .setStatus(active ? NetworkSliceInfo.SLICE_STATUS_ALLOWED
+                        : NetworkSliceInfo.SLICE_STATUS_UNKNOWN)
+                .setSliceServiceType(sliceServiceType)
+                .build();
+    }
+
+    @NonNull private TrafficDescriptor createTrafficDescriptor(@NonNull String appId) {
+        TrafficDescriptor.OsAppId osAppId = new TrafficDescriptor.OsAppId(
+                TrafficDescriptor.OsAppId.ANDROID_OS_ID, appId);
+        return new TrafficDescriptor.Builder()
+                .setOsAppId(osAppId.getBytes())
+                .build();
+    }
+
+    @NonNull private RouteSelectionDescriptor createRouteSelectionDescriptor(
+            @NonNull List<NetworkSliceInfo> sliceInfos) {
+        return new RouteSelectionDescriptor(
+                RouteSelectionDescriptor.MIN_ROUTE_PRECEDENCE,
+                RouteSelectionDescriptor.SESSION_TYPE_IPV4,
+                RouteSelectionDescriptor.ROUTE_SSC_MODE_1,
+                sliceInfos, Collections.emptyList());
+    }
+
+    @NonNull private NetworkSlicingConfig createNetworkSlicingConfig(
+            @NonNull List<NetworkSliceInfo> sliceInfos,
+            @NonNull List<TrafficDescriptor> trafficDescriptors,
+            @NonNull List<RouteSelectionDescriptor> routeSelectionDescriptors) {
+        UrspRule urspRule = new UrspRule(0, trafficDescriptors, routeSelectionDescriptors);
+        return new NetworkSlicingConfig(Collections.singletonList(urspRule), sliceInfos);
+    }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 4f9b879..9e1ea24 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -35,6 +35,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -2899,6 +2900,36 @@
                 disconnectCause.getTelephonyDisconnectCause());
     }
 
+    @Test
+    public void testNormalCallUsingNonTerrestrialNetwork() {
+        setupForCallTest();
+        // Call is not supported while using satellite
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
+        DisconnectCause disconnectCause = mConnection.getDisconnectCause();
+        assertEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+                disconnectCause.getTelephonyDisconnectCause());
+
+        // Call is supported while using satellite
+        nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_VOICE))
+                .build();
+        ss.addNetworkRegistrationInfo(nri);
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", "TC@2"));
+        disconnectCause = mConnection.getDisconnectCause();
+        assertNotEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+                disconnectCause.getTelephonyDisconnectCause());
+    }
+
     private void setupForDialForDomainSelection(Phone mockPhone, int domain, boolean isEmergency) {
         if (isEmergency) {
             doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)