Merge "TelecomAccountRegistry should be consistent in operator name retrieval"
diff --git a/Android.bp b/Android.bp
index 9a7a5b0..dc35c5d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,6 +43,7 @@
         "PlatformProperties",
         "modules-utils-os",
         "nist-sip",
+        "service-entitlement"
     ],
 
     srcs: [
@@ -82,8 +83,11 @@
 // Allow other applications to use public constants from SlicePurchaseController
 java_library {
     name: "SlicePurchaseController",
-    srcs: ["src/com/android/phone/slice/SlicePurchaseController.java",],
-    libs: ["telephony-common"],
+    srcs: ["src/com/android/phone/slice/*.java",],
+    libs: [
+        "telephony-common",
+        "service-entitlement"
+        ],
 }
 
 platform_compat_config {
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bf6872e..3d80246 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -159,9 +159,6 @@
     <!-- Needed to block unwanted malicious pop up overlays -->
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
 
-    <!-- Needed to set user association to a certain sim -->
-    <uses-permission android:name="android.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION"/>
-
     <permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"
                 android:label="Access last known cell identity."
                 android:protectionLevel="signature"/>
diff --git a/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto b/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
index 528e4b0..088b5b7 100644
--- a/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
+++ b/ecc/conversion_toolset_v1/proto/protobuf_ecc_data.proto
@@ -19,6 +19,12 @@
         AIEC = 7;
     }
 
+    enum Routing {
+        UNKNOWN = 0;
+        EMERGENCY = 1;
+        NORMAL= 2;
+    }
+
     // Required: Every EccInfo shall contain a phone number.
     optional string phone_number = 1;
 
@@ -26,11 +32,11 @@
     repeated Type types = 2 [packed=true];
 
 
-    //Optional: By default, the emergency number is assumed to be 'emergency routed'
-    optional bool is_normal_routed = 3 [default = false];
+    //Optional: By default, routing is assumed to be 'UNKNOWN'
+    optional Routing routing = 3 [default = UNKNOWN];
 
-    //Optional: This field is evaluated only if is_normal_routed is set to true
-    //If the field is empty, normal routing is used for all MNCs
+    //Optional: This field is evaluated only if routing is set to NORMAL
+    //If the field is empty, NORMAL routing is used for all MNCs
     //Else normal routing is used only for list of MNCs specified
     repeated string normal_routing_mncs = 4;
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 27d1de3..7b5cf81 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -17,6 +17,7 @@
 package com.android.phone;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
 
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
@@ -9779,12 +9780,22 @@
 
     /**
      * Get the IRadio HAL Version
+     * @deprecated use getHalVersion instead
      */
+    @Deprecated
     @Override
     public int getRadioHalVersion() {
+        return getHalVersion(HAL_SERVICE_RADIO);
+    }
+
+    /**
+     * Get the HAL Version of a specific service
+     */
+    @Override
+    public int getHalVersion(int service) {
         Phone phone = getDefaultPhone();
         if (phone == null) return -1;
-        HalVersion hv = phone.getHalVersion();
+        HalVersion hv = phone.getHalVersion(service);
         if (hv.equals(HalVersion.UNKNOWN)) return -1;
         return hv.major * 100 + hv.minor;
     }
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index a4ee836..d0aad4a 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -31,7 +31,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
-import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.telecom.PhoneAccount;
@@ -703,7 +702,7 @@
     }
 
     public static PhoneAccountHandle makePstnPhoneAccountHandle(String id) {
-        return makePstnPhoneAccountHandleWithPrefix(id, "", false, null);
+        return makePstnPhoneAccountHandleWithPrefix(id, "", false);
     }
 
     public static PhoneAccountHandle makePstnPhoneAccountHandle(int phoneId) {
@@ -711,26 +710,22 @@
     }
 
     public static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
-        return makePstnPhoneAccountHandleWithPrefix(phone, "", false, null);
+        return makePstnPhoneAccountHandleWithPrefix(phone, "", false);
     }
 
     public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(
-            Phone phone, String prefix, boolean isEmergency, UserHandle userHandle) {
+            Phone phone, String prefix, boolean isEmergency) {
         // TODO: Should use some sort of special hidden flag to decorate this account as
         // an emergency-only account
         String id = isEmergency ? EMERGENCY_ACCOUNT_HANDLE_ID : prefix +
                 String.valueOf(phone.getSubId());
-        return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency, userHandle);
+        return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency);
     }
 
     public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(
-            String id, String prefix, boolean isEmergency, UserHandle userHandle) {
+            String id, String prefix, boolean isEmergency) {
         ComponentName pstnConnectionServiceName = getPstnConnectionServiceName();
-        // If user handle is null, resort to default constructor to use phone process's
-        // user handle
-        return userHandle == null
-                ? new PhoneAccountHandle(pstnConnectionServiceName, id)
-                : new PhoneAccountHandle(pstnConnectionServiceName, id, userHandle);
+        return new PhoneAccountHandle(pstnConnectionServiceName, id);
     }
 
     public static int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index b6aaebe..49e1379 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -387,7 +387,7 @@
 
     private PhoneAccountHandle getEmergencyPhoneAccount() {
         return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                (Phone) null, "" /* prefix */, true /* isEmergency */, null /* userHandle */);
+                (Phone) null, "" /* prefix */, true /* isEmergency */);
     }
 
     public static Intent buildPhoneAccountConfigureIntent(
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
new file mode 100644
index 0000000..3fa64df
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.slice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.provider.DeviceConfig;
+import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.libraries.entitlement.CarrierConfig;
+import com.android.libraries.entitlement.ServiceEntitlement;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.UUID;
+
+/**
+ * Premium network entitlement API class to check the premium network slice entitlement result
+ * from carrier API over the network.
+ */
+public class PremiumNetworkEntitlementApi {
+    private static final String TAG = "PremiumNwEntitlementApi";
+    private static final String ENTITLEMENT_STATUS_KEY = "EntitlementStatus";
+    private static final String PROVISION_STATUS_KEY = "ProvisionStatus";
+    private static final String SERVICE_FLOW_URL_KEY = "ServiceFlow_URL";
+    private static final String PROVISION_TIME_LEFT_KEY = "ProvisionTimeLeft";
+    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.
+     */
+    private static final String UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR =
+            "f2b0661a-9114-4b1b-9add-a8d338f9c054";
+
+    /**
+     * Experiment flag to enable bypassing EAP-AKA authentication for Slice Purchase activities.
+     * The device will accept any challenge from the entitlement server and return a predefined
+     * string as a response.
+     *
+     * This flag should be enabled for testing only.
+     */
+    public static final String BYPASS_EAP_AKA_AUTH_FOR_SLICE_PURCHASE_ENABLED =
+            "bypass_eap_aka_auth_for_slice_purchase_enabled";
+
+    @NonNull private final Phone mPhone;
+    @NonNull private final ServiceEntitlement mServiceEntitlement;
+
+    public PremiumNetworkEntitlementApi(@NonNull Phone phone,
+            @NonNull PersistableBundle carrierConfig) {
+        mPhone = phone;
+        if (isBypassEapAkaAuthForSlicePurchaseEnabled()) {
+            mServiceEntitlement =
+                    new ServiceEntitlement(
+                            mPhone.getContext(),
+                            getEntitlementServerCarrierConfig(carrierConfig),
+                            mPhone.getSubId(),
+                            true,
+                            DEFAULT_EAP_AKA_RESPONSE);
+        } else {
+            mServiceEntitlement =
+                    new ServiceEntitlement(
+                            mPhone.getContext(),
+                            getEntitlementServerCarrierConfig(carrierConfig),
+                            mPhone.getSubId());
+        }
+    }
+
+    /**
+     * Returns premium network slice entitlement check result from carrier API (over network),
+     * or {@code null} on unrecoverable network issue or malformed server response.
+     * This is blocking call sending HTTP request and should not be called on main thread.
+     */
+    @Nullable public PremiumNetworkEntitlementResponse checkEntitlementStatus(
+            @TelephonyManager.PremiumCapability int capability) {
+        Log.d(TAG, "checkEntitlementStatus subId=" + mPhone.getSubId());
+        ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
+        // Set fake device info to avoid leaking
+        requestBuilder.setTerminalVendor("vendorX");
+        requestBuilder.setTerminalModel("modelY");
+        requestBuilder.setTerminalSoftwareVersion("versionZ");
+        requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+        requestBuilder.setNetworkIdentifier(
+                TelephonyManager.convertPremiumCapabilityToString(capability));
+        ServiceEntitlementRequest request = requestBuilder.build();
+        PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
+                new PremiumNetworkEntitlementResponse();
+
+        String response = null;
+        try {
+            response = mServiceEntitlement.queryEntitlementStatus(
+                    ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE,
+                    request);
+        } catch (ServiceEntitlementException e) {
+            Log.e(TAG, "queryEntitlementStatus failed", e);
+            reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+                    "checkEntitlementStatus failed with ServiceEntitlementException");
+        }
+        if (response == null) {
+            return null;
+        }
+        try {
+            JSONObject jsonAuthResponse = new JSONObject(response);
+            String entitlementStatus = null;
+            String provisionStatus = null;
+            String provisionTimeLeft = null;
+            if (jsonAuthResponse.has(ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE)) {
+                JSONObject jsonToken = jsonAuthResponse.getJSONObject(
+                        ServiceEntitlement.APP_PREMIUM_NETWORK_SLICE);
+                if (jsonToken.has(ENTITLEMENT_STATUS_KEY)) {
+                    entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
+                    if (entitlementStatus == null) {
+                        return null;
+                    }
+                    premiumNetworkEntitlementResponse.mEntitlementStatus =
+                            Integer.parseInt(entitlementStatus);
+                }
+                if (jsonToken.has(PROVISION_STATUS_KEY)) {
+                    provisionStatus = jsonToken.getString(PROVISION_STATUS_KEY);
+                    if (provisionStatus != null) {
+                        premiumNetworkEntitlementResponse.mProvisionStatus =
+                                Integer.parseInt(provisionStatus);
+                    }
+                }
+                if (jsonToken.has(PROVISION_TIME_LEFT_KEY)) {
+                    provisionTimeLeft = jsonToken.getString(PROVISION_TIME_LEFT_KEY);
+                    if (provisionTimeLeft != null) {
+                        premiumNetworkEntitlementResponse.mEntitlementStatus =
+                                Integer.parseInt(provisionTimeLeft);
+                    }
+                }
+                if (jsonToken.has(SERVICE_FLOW_URL_KEY)) {
+                    provisionStatus = jsonToken.getString(SERVICE_FLOW_URL_KEY);
+                    if (provisionStatus != null) {
+                        premiumNetworkEntitlementResponse.mProvisionStatus =
+                                Integer.parseInt(provisionStatus);
+                    }
+                    premiumNetworkEntitlementResponse.mServiceFlowURL =
+                            jsonToken.getString(SERVICE_FLOW_URL_KEY);
+                }
+            }
+
+
+        } catch (JSONException e) {
+            Log.e(TAG, "queryEntitlementStatus failed", e);
+            reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+                    "checkEntitlementStatus failed with JSONException");
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "queryEntitlementStatus failed", e);
+            reportAnomaly(UUID_ENTITLEMENT_CHECK_UNEXPECTED_ERROR,
+                    "checkEntitlementStatus failed with NumberFormatException");
+        }
+
+        return premiumNetworkEntitlementResponse;
+    }
+
+    private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
+        AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
+    }
+
+    /**
+     * Returns entitlement server url from the given carrier configs or a default empty string
+     * if it is not available.
+     */
+    @NonNull public static String getEntitlementServerUrl(
+            @NonNull PersistableBundle carrierConfig) {
+        return carrierConfig.getString(
+                CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+                "");
+    }
+
+    @NonNull private CarrierConfig getEntitlementServerCarrierConfig(
+            @NonNull PersistableBundle carrierConfig) {
+        String entitlementServiceUrl = getEntitlementServerUrl(carrierConfig);
+        return CarrierConfig.builder().setServerUrl(entitlementServiceUrl).build();
+    }
+
+    private boolean isBypassEapAkaAuthForSlicePurchaseEnabled() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
+                BYPASS_EAP_AKA_AUTH_FOR_SLICE_PURCHASE_ENABLED, false);
+    }
+}
diff --git a/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
new file mode 100644
index 0000000..d852a69
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.slice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+/**
+ * Response class containing the entitlement status, provisioning status, and service flow URL
+ * for premium network entitlement checks.
+ */
+public class PremiumNetworkEntitlementResponse {
+    public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED = 0;
+    public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED = 1;
+    public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE = 2;
+    public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING = 3;
+    public static final int PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED = 4;
+
+    @IntDef(prefix = {"PREMIUM_NETWORK_ENTITLEMENT_STATUS_"},
+            value = {
+                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED,
+                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED,
+                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE,
+                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING,
+                    PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED
+            })
+    public @interface PremiumNetworkEntitlementStatus {}
+
+    public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED = 0;
+    public static final int PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED = 1;
+    public static final int PREMIUM_NETWORK_PROVISION_STATUS_NOT_REQUIRED = 2;
+    public static final int PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS = 3;
+
+    @IntDef(prefix = {"PREMIUM_NETWORK_PROVISION_STATUS_"},
+            value = {
+                    PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED,
+                    PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED,
+                    PREMIUM_NETWORK_PROVISION_STATUS_NOT_REQUIRED,
+                    PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+            })
+    public @interface PremiumNetworkProvisionStatus {}
+
+    @PremiumNetworkEntitlementStatus public int mEntitlementStatus;
+    @PremiumNetworkProvisionStatus public int mProvisionStatus;
+    @NonNull public String mServiceFlowURL;
+
+    /**
+     * @return {@code true} if the premium network is provisioned and {@code false} otherwise.
+     */
+    public boolean isProvisioned() {
+        return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED
+                || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED;
+    }
+
+    /**
+     * @return {@code true} if provisioning the premium network is in progress and
+     *         {@code false} otherwise.
+     */
+    public boolean isProvisioningInProgress() {
+        return mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+                || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING;
+    }
+
+    /**
+     * @return {@code true} if the premium network capability is allowed and
+     *         {@code false} otherwise.
+     */
+    public boolean isPremiumNetworkCapabilityAllowed() {
+        switch (mEntitlementStatus) {
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE:
+            case PREMIUM_NETWORK_ENTITLEMENT_STATUS_DISABLED:
+                return false;
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
index cdaba41..5c3b63e 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -16,6 +16,12 @@
 
 package com.android.phone.slice;
 
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +34,8 @@
 import android.net.ConnectivityManager;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.AnomalyReporter;
@@ -40,6 +48,7 @@
 import android.util.Log;
 import android.webkit.WebView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 
 import java.lang.annotation.Retention;
@@ -149,8 +158,9 @@
     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED =
             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED";
     /** Action indicating the purchase request was not made on the default data subscription. */
-    private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUB =
-            "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUB";
+    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 purchase request was successful. */
     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS =
             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS";
@@ -206,10 +216,10 @@
      * Extra for the not-default data subscription ID PendingIntent that the slice purchase
      * application can send as a response if the premium capability purchase request failed because
      * it was not requested on the default data subscription.
-     * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUB}.
+     * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION}.
      */
-    public static final String EXTRA_INTENT_NOT_DEFAULT_DATA_SUB =
-            "com.android.phone.slice.extra.INTENT_NOT_DEFAULT_DATA_SUB";
+    public static final String EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION =
+            "com.android.phone.slice.extra.INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION";
     /**
      * Extra for the success PendingIntent that the slice purchase application can send as a
      * response if the premium capability purchase request was successful.
@@ -245,6 +255,8 @@
             mSlicePurchaseControllerBroadcastReceivers = new HashMap<>();
     /** The current network slicing configuration. */
     @Nullable private NetworkSlicingConfig mSlicingConfig;
+    /** Premium network entitlement query API */
+    @NonNull private final PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
 
     private class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
         @TelephonyManager.PremiumCapability private final int mCapability;
@@ -281,7 +293,7 @@
                     logd("Slice purchase application canceled for capability: "
                             + TelephonyManager.convertPremiumCapabilityToString(capability));
                     SlicePurchaseController.getInstance(phoneId)
-                            .sendPurchaseResultFromSlicePurchaseApp(capability,
+                            .handlePurchaseResult(capability,
                             TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED,
                             true);
                     break;
@@ -297,19 +309,18 @@
                     logd("Purchase premium capability request failed for capability: "
                             + TelephonyManager.convertPremiumCapabilityToString(capability));
                     SlicePurchaseController.getInstance(phoneId)
-                            .sendPurchaseResultFromSlicePurchaseApp(capability,
+                            .handlePurchaseResult(capability,
                             TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED,
                             false);
                     break;
                 }
-                case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUB: {
+                case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION: {
                     logd("Purchase premium capability request was not made on the default data "
                             + "subscription for capability: "
                             + TelephonyManager.convertPremiumCapabilityToString(capability));
                     SlicePurchaseController.getInstance(phoneId)
-                            .sendPurchaseResultFromSlicePurchaseApp(capability,
-                            TelephonyManager
-                                    .PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB,
+                            .handlePurchaseResult(capability,
+                            PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                             false);
                     break;
                 }
@@ -340,7 +351,9 @@
         //  that dismiss notifications and update SlicePurchaseController instance
         int phoneId = phone.getPhoneId();
         if (sInstances.get(phoneId) == null) {
-            sInstances.put(phoneId, new SlicePurchaseController(phone));
+            HandlerThread handlerThread = new HandlerThread("SlicePurchaseController");
+            handlerThread.start();
+            sInstances.put(phoneId, new SlicePurchaseController(phone, handlerThread.getLooper()));
         }
         return sInstances.get(phoneId);
     }
@@ -356,11 +369,14 @@
         return sInstances.get(phoneId);
     }
 
-    private SlicePurchaseController(@NonNull Phone phone) {
-        super(phone.getLooper());
+    @VisibleForTesting
+    public SlicePurchaseController(@NonNull Phone phone, @NonNull Looper looper) {
+        super(looper);
         mPhone = phone;
         // TODO: Create a cached value for slicing config in DataIndication and initialize here
         mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICING_CONFIG_CHANGED, null);
+        mPremiumNetworkEntitlementApi = new PremiumNetworkEntitlementApi(mPhone,
+                getCarrierConfigs());
     }
 
     @Override
@@ -458,19 +474,19 @@
         }
         if (!isPremiumCapabilitySupportedByCarrier(capability)) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
                     onComplete);
             return;
         }
         if (!isDefaultDataSub()) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                     onComplete);
             return;
         }
         if (isSlicingConfigActive(capability)) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                     onComplete);
             return;
         }
@@ -492,17 +508,10 @@
                     onComplete);
             return;
         }
-        if (isNetworkCongested(capability)) {
-            throttleCapability(capability, getThrottleDuration(
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED));
-            sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED,
-                    onComplete);
-            return;
-        }
+
         if (mPendingPurchaseCapabilities.containsKey(capability)) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
                     onComplete);
             return;
         }
@@ -525,7 +534,7 @@
         onComplete.sendToTarget();
     }
 
-    private void sendPurchaseResultFromSlicePurchaseApp(
+    private void handlePurchaseResult(
             @TelephonyManager.PremiumCapability int capability,
             @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle) {
         mPhone.getContext().unregisterReceiver(
@@ -569,6 +578,37 @@
 
     private void onDisplayBoosterNotification(@TelephonyManager.PremiumCapability int capability,
             @NonNull String appName) {
+        PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
+                mPremiumNetworkEntitlementApi.checkEntitlementStatus(capability);
+
+        /* invalid response for entitlement check */
+        if (premiumNetworkEntitlementResponse == null) {
+            logd("Invalid response for entitlement check.");
+            handlePurchaseResult(capability,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED, true);
+            return;
+        }
+
+        if (premiumNetworkEntitlementResponse.isProvisioned()) {
+            logd("Entitlement Check: Already provisioned.");
+            handlePurchaseResult(capability,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED, true);
+            return;
+        }
+
+        if (premiumNetworkEntitlementResponse.isProvisioningInProgress()) {
+            logd("Entitlement Check: In Progress");
+            handlePurchaseResult(capability,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS, true);
+            return;
+        }
+
+        if (!premiumNetworkEntitlementResponse.isPremiumNetworkCapabilityAllowed()) {
+            handlePurchaseResult(capability,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED, true);
+            return;
+        }
+
         // Start timeout for purchase completion.
         long timeout = getCarrierConfigs().getLong(CarrierConfigManager
                 .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG);
@@ -590,8 +630,9 @@
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR, capability, true));
         intent.putExtra(EXTRA_INTENT_REQUEST_FAILED, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED, capability, false));
-        intent.putExtra(EXTRA_INTENT_NOT_DEFAULT_DATA_SUB, createPendingIntent(
-                ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUB, capability, false));
+        intent.putExtra(EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION, createPendingIntent(
+                ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION, capability,
+                false));
         intent.putExtra(EXTRA_INTENT_SUCCESS, createPendingIntent(
                 ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS, capability, true));
         logd("Broadcasting start intent to SlicePurchaseBroadcastReceiver.");
@@ -604,7 +645,7 @@
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED);
         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_SUB);
+        filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION);
         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS);
         mPhone.getContext().registerReceiver(
                 mSlicePurchaseControllerBroadcastReceivers.get(capability), filter);
@@ -639,7 +680,7 @@
         logd("Broadcasting timeout intent to SlicePurchaseBroadcastReceiver.");
         mPhone.getContext().sendBroadcast(intent);
 
-        sendPurchaseResultFromSlicePurchaseApp(
+        handlePurchaseResult(
                 capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, true);
     }
 
@@ -652,7 +693,7 @@
             reportAnomaly(UUID_UNKNOWN_FAILURE_CODE,
                     "Failure code needs to be added for: " + failureReason);
         }
-        sendPurchaseResultFromSlicePurchaseApp(capability,
+        handlePurchaseResult(capability,
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, true);
     }
 
@@ -667,7 +708,7 @@
         logd("Waiting " + TimeUnit.MILLISECONDS.toMinutes(setupDuration) + " minutes for the "
                 + "network to set up the slicing configuration.");
         sendMessageDelayed(obtainMessage(EVENT_SETUP_TIMEOUT, capability), setupDuration);
-        sendPurchaseResultFromSlicePurchaseApp(
+        handlePurchaseResult(
                 capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, false);
     }
 
@@ -693,7 +734,7 @@
             return getCarrierConfigs().getLong(CarrierConfigManager
                     .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
         }
-        if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED
+        if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED
                 || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR) {
             return getCarrierConfigs().getLong(CarrierConfigManager
                     .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
@@ -769,11 +810,6 @@
         return false;
     }
 
-    private boolean isNetworkCongested(@TelephonyManager.PremiumCapability int capability) {
-        // TODO: Implement TS43
-        return true;
-    }
-
     /**
      * Returns the failure code {@link FailureCode} as a String.
      *
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 453cb55..8615325 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -403,7 +403,7 @@
         // receives an MT call while in ECM. Use the Emergency PhoneAccount to receive the account
         // if it exists.
         PhoneAccountHandle emergencyHandle =
-                PhoneUtils.makePstnPhoneAccountHandleWithPrefix(mPhone, "", true, null);
+                PhoneUtils.makePstnPhoneAccountHandleWithPrefix(mPhone, "", true);
         if(telecomAccountRegistry.hasAccountEntryForPhoneAccount(emergencyHandle)) {
             Log.i(this, "Receiving MT call in ECM. Using Emergency PhoneAccount Instead.");
             return emergencyHandle;
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 324fb85..3d2fb85 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -285,19 +285,13 @@
         private PhoneAccount buildPstnPhoneAccount(boolean isEmergency, boolean isTestAccount) {
             String testPrefix = isTestAccount ? "Test " : "";
 
-            // Check if we are registering another user. If we are, ensure that the account
-            // is registered to that user handle.
-            int subId = mPhone.getSubId();
-            UserHandle userToRegister = mSubscriptionManager.isActiveSubscriptionId(subId)
-                    ? mSubscriptionManager.getSubscriptionUserHandle(subId)
-                    : null;
-
             // Build the Phone account handle.
             PhoneAccountHandle phoneAccountHandle =
                     PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                            mPhone, testPrefix, isEmergency, userToRegister);
+                            mPhone, testPrefix, isEmergency);
 
             // Populate the phone account data.
+            int subId = mPhone.getSubId();
             String subscriberId = mPhone.getSubscriberId();
             int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
             int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
@@ -363,12 +357,8 @@
 
             // By default all SIM phone accounts can place emergency calls.
             int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
-                    PhoneAccount.CAPABILITY_CALL_PROVIDER;
-
-            // This is enabled by default. To support work profiles, it should not be enabled.
-            if (userToRegister == null) {
-                capabilities |= PhoneAccount.CAPABILITY_MULTI_USER;
-            }
+                    PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                    PhoneAccount.CAPABILITY_MULTI_USER;
 
             if (mContext.getResources().getBoolean(R.bool.config_pstnCanPlaceEmergencyCalls)) {
                 capabilities |= PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 5b1f463..cb1277f 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -211,6 +213,7 @@
     @VisibleForTesting
     public interface SubscriptionManagerProxy {
         int getDefaultVoicePhoneId();
+        int getDefaultDataPhoneId();
         int getSimStateForSlotIdx(int slotId);
         int getPhoneId(int subId);
     }
@@ -222,6 +225,11 @@
         }
 
         @Override
+        public int getDefaultDataPhoneId() {
+            return getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
+        }
+
+        @Override
         public int getSimStateForSlotIdx(int slotId) {
             return SubscriptionManager.getSimStateForSlotIndex(slotId);
         }
@@ -360,8 +368,7 @@
         @Override
         public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
                 boolean isEmergency) {
-            return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                    phone, prefix, isEmergency, null);
+            return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
         }
     };
 
@@ -824,7 +831,8 @@
                     // an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
                     // be handled at the RIL/vendor level by emergencyDial(...).
                     boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
-                            && phone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+                            && phone.getHalVersion(HAL_SERVICE_VOICE)
+                            .less(RIL.RADIO_HAL_VERSION_1_4);
                     if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
                         // We currently only look to make sure that the radio is on before dialing.
                         // We should be able to make emergency calls at any time after the radio has
@@ -2166,39 +2174,57 @@
     /**
      * Retrieves the most sensible Phone to use for an emergency call using the following Priority
      *  list (for multi-SIM devices):
-     *  1) The User's SIM preference for Voice calling
-     *  2) The First Phone that is currently IN_SERVICE or is available for emergency calling
-     *  3) Prioritize phones that have the dialed emergency number as part of their emergency
+     *  1) The Phone that is in emergency SMS mode
+     *  2) The phone based on User's SIM preference of Voice calling or Data in order
+     *  3) The First Phone that is currently IN_SERVICE or is available for emergency calling
+     *  4) Prioritize phones that have the dialed emergency number as part of their emergency
      *     number list
-     *  4) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
-     *     are locked, skip to condition 5).
-     *  5) The Phone with more Capabilities.
-     *  6) The First Phone that has a SIM card in it (Starting from Slot 0...N)
-     *  7) The Default Phone (Currently set as Slot 0)
+     *  5) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
+     *     are locked, skip to condition 6).
+     *  6) The Phone with more Capabilities.
+     *  7) The First Phone that has a SIM card in it (Starting from Slot 0...N)
+     *  8) The Default Phone (Currently set as Slot 0)
      */
     @VisibleForTesting
+    @NonNull
     public Phone getFirstPhoneForEmergencyCall(List<Phone> phonesWithEmergencyNumber) {
-        // 1)
+        int phoneCount = mTelephonyManagerProxy.getPhoneCount();
+        for (int i = 0; i < phoneCount; i++) {
+            Phone phone = mPhoneFactoryProxy.getPhone(i);
+            // 1)
+            if (phone != null && phone.isInEmergencySmsMode()) {
+                if (isAvailableForEmergencyCalls(phone)) {
+                    if (phonesWithEmergencyNumber == null
+                            || phonesWithEmergencyNumber.contains(phone)) {
+                        return phone;
+                    }
+                }
+            }
+        }
+
+        // 2)
         int phoneId = mSubscriptionManagerProxy.getDefaultVoicePhoneId();
+        if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+            phoneId = mSubscriptionManagerProxy.getDefaultDataPhoneId();
+        }
         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
-            Phone defaultPhone = mPhoneFactoryProxy.getPhone(phoneId);
-            if (defaultPhone != null && isAvailableForEmergencyCalls(defaultPhone)) {
+            Phone selectedPhone = mPhoneFactoryProxy.getPhone(phoneId);
+            if (selectedPhone != null && isAvailableForEmergencyCalls(selectedPhone)) {
                 if (phonesWithEmergencyNumber == null
-                        || phonesWithEmergencyNumber.contains(defaultPhone)) {
-                    return defaultPhone;
+                        || phonesWithEmergencyNumber.contains(selectedPhone)) {
+                    return selectedPhone;
                 }
             }
         }
 
         Phone firstPhoneWithSim = null;
-        int phoneCount = mTelephonyManagerProxy.getPhoneCount();
         List<SlotStatus> phoneSlotStatus = new ArrayList<>(phoneCount);
         for (int i = 0; i < phoneCount; i++) {
             Phone phone = mPhoneFactoryProxy.getPhone(i);
             if (phone == null) {
                 continue;
             }
-            // 2)
+            // 3)
             if (isAvailableForEmergencyCalls(phone)) {
                 if (phonesWithEmergencyNumber == null
                         || phonesWithEmergencyNumber.contains(phone)) {
@@ -2208,7 +2234,7 @@
                     return phone;
                 }
             }
-            // 5)
+            // 6)
             // Store the RAF Capabilities for sorting later.
             int radioAccessFamily = phone.getRadioAccessFamily();
             SlotStatus status = new SlotStatus(i, radioAccessFamily, phone.getSubId());
@@ -2216,7 +2242,7 @@
             Log.i(this, "getFirstPhoneForEmergencyCall, RAF:" +
                 Integer.toHexString(radioAccessFamily) + " saved for Phone Id:" + i + " subId:"
                 + phone.getSubId());
-            // 4)
+            // 5)
             // Report Slot's PIN/PUK lock status for sorting later.
             int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
             // Record SimState.
@@ -2226,7 +2252,7 @@
                 status.isLocked = true;
             }
 
-            // 3) Store if the Phone has the corresponding emergency number
+            // 4) Store if the Phone has the corresponding emergency number
             if (phonesWithEmergencyNumber != null) {
                 for (Phone phoneWithEmergencyNumber : phonesWithEmergencyNumber) {
                     if (phoneWithEmergencyNumber != null
@@ -2235,7 +2261,7 @@
                     }
                 }
             }
-            // 6)
+            // 7)
             if (firstPhoneWithSim == null &&
                 (phone.getSubId() != SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
                 // The slot has a SIM card inserted (and an active subscription), but is not in
@@ -2247,16 +2273,21 @@
                     firstPhoneWithSim.getPhoneId());
             }
         }
-        // 7)
+        // 8)
         if (firstPhoneWithSim == null && phoneSlotStatus.isEmpty()) {
-            if (phonesWithEmergencyNumber == null || phonesWithEmergencyNumber.isEmpty()) {
-                // No Phones available, get the default
-                Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
-                return  mPhoneFactoryProxy.getDefaultPhone();
+            if (phonesWithEmergencyNumber != null) {
+                for (Phone phoneWithEmergencyNumber : phonesWithEmergencyNumber) {
+                    if (phoneWithEmergencyNumber != null) {
+                        return phoneWithEmergencyNumber;
+                    }
+                }
             }
-            return phonesWithEmergencyNumber.get(0);
+
+            // No Phones available, get the default
+            Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
+            return  mPhoneFactoryProxy.getDefaultPhone();
         } else {
-            // 5)
+            // 6)
             final int defaultPhoneId = mPhoneFactoryProxy.getDefaultPhone().getPhoneId();
             final Phone firstOccupiedSlot = firstPhoneWithSim;
             if (!phoneSlotStatus.isEmpty()) {
@@ -2319,7 +2350,7 @@
                         "with highest capability");
                 return mPhoneFactoryProxy.getPhone(mostCapablePhoneId);
             } else {
-                // 6)
+                // 7)
                 return firstPhoneWithSim;
             }
         }
diff --git a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
index 2eeed30..9dc4732 100644
--- a/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
+++ b/testapps/TestSliceApp/app/src/main/java/com/google/android/sample/testsliceapp/PrioritizeLatency.java
@@ -19,10 +19,10 @@
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED;
-import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE;
-import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB;
+import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_OVERRIDDEN;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP;
 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS;
@@ -270,10 +270,10 @@
                 return "Feature not supported";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE:
                 return "Network not available";
-            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED:
-                return "Network congested";
-            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB:
-                return "No default data";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED:
+                return "Entitlement check failed";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION:
+                return "Not default data subscription";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP:
                 return "Pending network setup";
             default:
diff --git a/tests/src/com/android/phone/PhoneUtilsTest.java b/tests/src/com/android/phone/PhoneUtilsTest.java
index b5ff0dc..521a0bb 100644
--- a/tests/src/com/android/phone/PhoneUtilsTest.java
+++ b/tests/src/com/android/phone/PhoneUtilsTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.os.UserHandle;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -83,15 +82,6 @@
         PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
                 PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString);
         assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                mPhoneAccountHandleIdString, "", false, null));
-    }
-
-    @Test
-    public void testMakePstnPhoneAccountHandleWithPrefixForAnotherUser() throws Exception {
-        UserHandle userHandle = new UserHandle(10);
-        PhoneAccountHandle phoneAccountHandleTest = new PhoneAccountHandle(
-                PSTN_CONNECTION_SERVICE_COMPONENT, mPhoneAccountHandleIdString, userHandle);
-        assertEquals(phoneAccountHandleTest, PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
-                mPhoneAccountHandleIdString, "", false, userHandle));
+                mPhoneAccountHandleIdString, "", false));
     }
 }
diff --git a/tests/src/com/android/phone/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/SlicePurchaseControllerTest.java
new file mode 100644
index 0000000..bfde6a4
--- /dev/null
+++ b/tests/src/com/android/phone/SlicePurchaseControllerTest.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.NetworkSlicingConfig;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.phone.slice.PremiumNetworkEntitlementApi;
+import com.android.phone.slice.PremiumNetworkEntitlementResponse;
+import com.android.phone.slice.SlicePurchaseController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class SlicePurchaseControllerTest extends TelephonyTestBase {
+    private static final String TAG = "SlicePurchaseControllerTest";
+    private static final String URL = "file:///android_asset/slice_purchase_test.html";
+    private static final int PHONE_ID = 0;
+    private static final long NOTIFICATION_TIMEOUT = 1000;
+    private static final long PURCHASE_CONDITION_TIMEOUT = 2000;
+    private static final long NETWORK_SETUP_TIMEOUT = 3000;
+    private static final long THROTTLE_TIMEOUT = 4000;
+
+    @Mock Phone mPhone;
+    @Mock Context mMockedContext;
+    @Mock CarrierConfigManager mCarrierConfigManager;
+    @Mock CommandsInterface mCommandsInterface;
+    @Mock ServiceState mServiceState;
+    @Mock PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
+
+    private SlicePurchaseController mSlicePurchaseController;
+    private PersistableBundle mBundle;
+    private PremiumNetworkEntitlementResponse mEntitlementResponse;
+    private Handler mHandler;
+    private TestableLooper mTestableLooper;
+    @TelephonyManager.PurchasePremiumCapabilityResult private int mResult;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        HandlerThread handlerThread = new HandlerThread("SlicePurchaseControllerTest");
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                AsyncResult ar = (AsyncResult) msg.obj;
+                mResult = (int) ar.result;
+            }
+        };
+        mTestableLooper = new TestableLooper(mHandler.getLooper());
+
+        doReturn(PHONE_ID).when(mPhone).getPhoneId();
+        doReturn(mMockedContext).when(mPhone).getContext();
+        doReturn(mServiceState).when(mPhone).getServiceState();
+        mPhone.mCi = mCommandsInterface;
+
+        doReturn(Context.CARRIER_CONFIG_SERVICE).when(mMockedContext)
+                .getSystemServiceName(eq(CarrierConfigManager.class));
+        doReturn(mCarrierConfigManager).when(mMockedContext)
+                .getSystemService(eq(Context.CARRIER_CONFIG_SERVICE));
+        mBundle = new PersistableBundle();
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        mSlicePurchaseController = new SlicePurchaseController(mPhone, mHandler.getLooper());
+        replaceInstance(SlicePurchaseController.class, "sInstances", mSlicePurchaseController,
+                Map.of(PHONE_ID, mSlicePurchaseController));
+        replaceInstance(SlicePurchaseController.class, "mPremiumNetworkEntitlementApi",
+                mSlicePurchaseController, mPremiumNetworkEntitlementApi);
+        mEntitlementResponse = new PremiumNetworkEntitlementResponse();
+        doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+                .checkEntitlementStatus(anyInt());
+    }
+
+    @Test
+    public void testIsPremiumCapabilityAvailableForPurchase() {
+        assertFalse(mSlicePurchaseController.isPremiumCapabilityAvailableForPurchase(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+
+        // all conditions met
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+        // retry to verify available
+        assertTrue(mSlicePurchaseController.isPremiumCapabilityAvailableForPurchase(
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultFeatureNotSupported() {
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+                mResult);
+
+        // retry after enabling feature
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+
+        tryPurchasePremiumCapability();
+        assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+                mResult);
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultCarrierDisabled() {
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, mResult);
+
+        // retry after enabling carrier configs
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+                mResult);
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultNotDefaultDataSubscription() {
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertEquals(
+                TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+                mResult);
+
+        // retry on default data subscription
+        doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+        tryPurchasePremiumCapability();
+        assertNotEquals(
+                TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+                mResult);
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultNetworkNotAvailable() {
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+                mResult);
+
+        // retry with valid network
+        doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+
+        tryPurchasePremiumCapability();
+        assertNotEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+                mResult);
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultEntitlementCheckFailed() throws Exception {
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+        doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+        doReturn(null).when(mPremiumNetworkEntitlementApi).checkEntitlementStatus(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
+                mResult);
+
+        // retry with provisioned response
+        mEntitlementResponse.mProvisionStatus =
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED;
+        doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+                .checkEntitlementStatus(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+                mResult);
+
+        // retry with provisioning response
+        mEntitlementResponse.mProvisionStatus =
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS;
+        doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+                .checkEntitlementStatus(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+                mResult);
+
+        // retry with disallowed response and throttling
+        mEntitlementResponse.mProvisionStatus =
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_PROVISION_STATUS_NOT_PROVISIONED;
+        mEntitlementResponse.mEntitlementStatus =
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCOMPATIBLE;
+        doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+                .checkEntitlementStatus(anyInt());
+        mBundle.putLong(CarrierConfigManager
+                .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+                PURCHASE_CONDITION_TIMEOUT);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
+                mResult);
+
+        // retry to verify throttled
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+        // retry with valid entitlement check to verify unthrottled
+        mTestableLooper.moveTimeForward(PURCHASE_CONDITION_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultAlreadyInProgress() {
+        sendValidPurchaseRequest();
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+                mResult);
+
+        // retry to verify same result
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+                mResult);
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultSuccess() throws Exception {
+        sendValidPurchaseRequest();
+
+        Intent intent = new Intent();
+        intent.setAction("com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        receiveSlicePurchaseResponse(intent);
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, mResult);
+
+        // retry tested in testPurchasePremiumCapabilityResultPendingNetworkSetup
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultPendingNetworkSetup() throws Exception {
+        testPurchasePremiumCapabilityResultSuccess();
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
+                mResult);
+
+        // retry to verify unthrottled
+        mTestableLooper.moveTimeForward(NETWORK_SETUP_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultAlreadyPurchased() throws Exception {
+        testPurchasePremiumCapabilityResultSuccess();
+
+        // TODO: implement slicing config logic properly
+        NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig(Collections.emptyList(),
+                Collections.singletonList(new NetworkSliceInfo.Builder()
+                        .setStatus(NetworkSliceInfo.SLICE_STATUS_ALLOWED).build()));
+        mSlicePurchaseController.obtainMessage(2 /* EVENT_SLICING_CONFIG_CHANGED */,
+                new AsyncResult(null, slicingConfig, null)).sendToTarget();
+        mTestableLooper.processAllMessages();
+
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+                mResult);
+
+        // retry to verify same result
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+                mResult);
+
+        // retry to verify purchase expired
+        slicingConfig = new NetworkSlicingConfig(Collections.emptyList(), Collections.emptyList());
+        mSlicePurchaseController.obtainMessage(2 /* EVENT_SLICING_CONFIG_CHANGED */,
+                new AsyncResult(null, slicingConfig, null)).sendToTarget();
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultTimeout() throws Exception {
+        sendValidPurchaseRequest();
+
+        mTestableLooper.moveTimeForward(NOTIFICATION_TIMEOUT);
+        mTestableLooper.processAllMessages();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, mResult);
+
+        // retry to verify throttled
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+        // retry to verify unthrottled
+        mTestableLooper.moveTimeForward(THROTTLE_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultUserCanceled() throws Exception {
+        sendValidPurchaseRequest();
+
+        Intent intent = new Intent();
+        intent.setAction("com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        receiveSlicePurchaseResponse(intent);
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED, mResult);
+
+        // retry to verify throttled
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+        // retry to verify unthrottled
+        mTestableLooper.moveTimeForward(THROTTLE_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultCarrierError() throws Exception {
+        sendValidPurchaseRequest();
+
+        Intent intent = new Intent();
+        intent.setAction(
+                "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        intent.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
+                SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE);
+        receiveSlicePurchaseResponse(intent);
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, mResult);
+
+        // retry to verify throttled
+        tryPurchasePremiumCapability();
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, mResult);
+
+        // retry to verify unthrottled
+        mTestableLooper.moveTimeForward(PURCHASE_CONDITION_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultRequestFailed() throws Exception {
+        sendValidPurchaseRequest();
+
+        Intent intent = new Intent();
+        intent.setAction(
+                "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        receiveSlicePurchaseResponse(intent);
+        assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED, mResult);
+
+        // retry to verify no throttling
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    @Test
+    public void testPurchasePremiumCapabilityResultNotDefaultDataSubscriptionResponse()
+            throws Exception {
+        sendValidPurchaseRequest();
+
+        Intent intent = new Intent();
+        intent.setAction("com.android.phone.slice.action."
+                + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION");
+        intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
+        intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+        receiveSlicePurchaseResponse(intent);
+        assertEquals(
+                TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+                mResult);
+
+        // retry to verify no throttling
+        testPurchasePremiumCapabilityResultSuccess();
+    }
+
+    private void sendValidPurchaseRequest() {
+        // feature supported
+        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
+                .getCachedAllowedNetworkTypesBitmask();
+        // carrier supported
+        mBundle.putIntArray(CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY,
+                new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY});
+        mBundle.putString(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, URL);
+        mBundle.putLong(CarrierConfigManager
+                .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG,
+                NOTIFICATION_TIMEOUT);
+        mBundle.putLong(CarrierConfigManager.KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG,
+                NETWORK_SETUP_TIMEOUT);
+        mBundle.putLong(CarrierConfigManager
+                .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+                THROTTLE_TIMEOUT);
+        mBundle.putLong(CarrierConfigManager
+                .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+                PURCHASE_CONDITION_TIMEOUT);
+        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        // default data subscription
+        doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mPhone).getSubId();
+        // network available
+        doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mServiceState).getDataNetworkType();
+        // entitlement check passed
+        mEntitlementResponse.mEntitlementStatus =
+                PremiumNetworkEntitlementResponse.PREMIUM_NETWORK_ENTITLEMENT_STATUS_ENABLED;
+        doReturn(mEntitlementResponse).when(mPremiumNetworkEntitlementApi)
+                .checkEntitlementStatus(anyInt());
+
+        tryPurchasePremiumCapability();
+        // verify that the purchase request was sent successfully and purchase timeout started
+        assertTrue(mSlicePurchaseController.hasMessages(4 /* EVENT_PURCHASE_TIMEOUT */,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY));
+    }
+
+    private void tryPurchasePremiumCapability() {
+        try {
+            mSlicePurchaseController.purchasePremiumCapability(
+                    TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
+                    mHandler.obtainMessage());
+            mTestableLooper.processAllMessages();
+        } catch (Exception expected) {
+            // SecurityException rethrown as RuntimeException when broadcasting intent
+            Log.d(TAG, expected.getMessage());
+        }
+        mTestableLooper.processAllMessages();
+    }
+
+    private void receiveSlicePurchaseResponse(Intent intent) throws Exception {
+        Class<?> receiverClass = Class.forName("com.android.phone.slice.SlicePurchaseController"
+                + "$SlicePurchaseControllerBroadcastReceiver");
+        Constructor<?> constructor = receiverClass.getDeclaredConstructor(
+                SlicePurchaseController.class, int.class);
+        constructor.setAccessible(true);
+        Object receiver = constructor.newInstance(mSlicePurchaseController,
+                TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+
+        Method method = receiver.getClass().getDeclaredMethod(
+                "onReceive", Context.class, Intent.class);
+        method.invoke(receiver, mMockedContext, intent);
+        mTestableLooper.processAllMessages();
+    }
+}
diff --git a/tests/src/com/android/phone/ecc/EccDataTest.java b/tests/src/com/android/phone/ecc/EccDataTest.java
index a52e2e7..baa4c7b 100644
--- a/tests/src/com/android/phone/ecc/EccDataTest.java
+++ b/tests/src/com/android/phone/ecc/EccDataTest.java
@@ -66,7 +66,7 @@
                 assertThat(loadedNumbers.contains(eccInfo.phoneNumber)).isFalse();
                 assertThat(eccInfo.types).isNotEmpty();
                 loadedNumbers.add(eccInfo.phoneNumber);
-                if (eccInfo.isNormalRouted) {
+                if (eccInfo.routing == ProtobufEccData.EccInfo.Routing.NORMAL) {
                     loadedMncs.clear();
                     for (String mnc : eccInfo.normalRoutingMncs) {
                         assertThat(mnc).isNotEmpty();
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index f2209db..553dbc9 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -192,6 +192,54 @@
     /**
      * Prerequisites:
      * - MSIM Device, two slots with SIMs inserted
+     * - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
+     * - Slot 1 is in Emergency SMS Mode
+     *
+     * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
+     */
+    @Test
+    @SmallTest
+    public void testEmergencySmsModeSimEmergencyOnly() {
+        Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                true /*isEmergencyOnly*/);
+        setDefaultPhone(slot0Phone);
+        setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+        setEmergencySmsMode(slot1Phone, true);
+
+        Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+        assertEquals(slot1Phone, resultPhone);
+    }
+
+    /**
+     * Prerequisites:
+     * - MSIM Device, two slots with SIMs inserted
+     * - Slot 0 is IN_SERVICE, Slot 1 is OUT_OF_SERVICE
+     * - Slot 1 is in Emergency SMS Mode
+     *
+     * Result: getFirstPhoneForEmergencyCall returns the slot 0 phone
+     */
+    @Test
+    @SmallTest
+    public void testEmergencySmsModeSimOutOfService() {
+        Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        setDefaultPhone(slot0Phone);
+        setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+        setEmergencySmsMode(slot1Phone, true);
+
+        Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+        assertEquals(slot0Phone, resultPhone);
+    }
+
+    /**
+     * Prerequisites:
+     * - MSIM Device, two slots with SIMs inserted
      * - Users default Voice SIM choice is IN_SERVICE
      *
      * Result: getFirstPhoneForEmergencyCall returns the default Voice SIM choice.
@@ -214,6 +262,52 @@
     /**
      * Prerequisites:
      * - MSIM Device, two slots with SIMs inserted
+     * - Users default data SIM choice is OUT_OF_SERVICE (emergency calls only)
+     *
+     * Result: getFirstPhoneForEmergencyCall returns the default data SIM choice.
+     */
+    @Test
+    @SmallTest
+    public void testDefaultDataSimEmergencyOnly() {
+        Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                true /*isEmergencyOnly*/);
+        setDefaultPhone(slot0Phone);
+        setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        setDefaultDataPhoneId(SLOT_1_PHONE_ID);
+
+        Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+        assertEquals(slot1Phone, resultPhone);
+    }
+
+    /**
+     * Prerequisites:
+     * - MSIM Device, two slots with SIMs inserted
+     * - Users default data SIM choice is OUT_OF_SERVICE
+     *
+     * Result: getFirstPhoneForEmergencyCall does not return the default data SIM choice.
+     */
+    @Test
+    @SmallTest
+    public void testDefaultDataSimOutOfService() {
+        Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        setDefaultPhone(slot0Phone);
+        setupDeviceConfig(slot0Phone, slot1Phone, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        setDefaultDataPhoneId(SLOT_1_PHONE_ID);
+
+        Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+        assertEquals(slot0Phone, resultPhone);
+    }
+
+    /**
+     * Prerequisites:
+     * - MSIM Device, two slots with SIMs inserted
      * - Slot 0 is OUT_OF_SERVICE, Slot 1 is OUT_OF_SERVICE (emergency calls only)
      *
      * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone
@@ -1615,10 +1709,18 @@
         when(mPhoneFactoryProxy.getPhone(eq(SLOT_1_PHONE_ID))).thenReturn(slot1Phone);
     }
 
+    private void setDefaultDataPhoneId(int defaultDataPhoneId) {
+        when(mSubscriptionManagerProxy.getDefaultDataPhoneId()).thenReturn(defaultDataPhoneId);
+    }
+
     private void setPhoneRadioAccessFamily(Phone phone, int radioAccessFamily) {
         when(phone.getRadioAccessFamily()).thenReturn(radioAccessFamily);
     }
 
+    private void setEmergencySmsMode(Phone phone, boolean isInEmergencySmsMode) {
+        when(phone.isInEmergencySmsMode()).thenReturn(isInEmergencySmsMode);
+    }
+
     private void setPhoneSlotState(int slotId, int slotState) {
         when(mSubscriptionManagerProxy.getSimStateForSlotIdx(slotId)).thenReturn(slotState);
     }