Query entitlement server for premium slice

Test: system test with test app. UT to be in separate CL
Bug: 257525164

Change-Id: Ief4114efbaaa4e476bdb4077d51da53c2dd03b17
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/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
new file mode 100644
index 0000000..de26955
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementApi.java
@@ -0,0 +1,204 @@
+/*
+ * 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.content.Context;
+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;
+
+class PremiumNetworkEntitlementApi {
+    private static final String TAG = "PremiumNetworkEntitlementApi";
+    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 PersistableBundle mCarrierConfig;
+    @NonNull private final ServiceEntitlement mServiceEntitlement;
+
+    PremiumNetworkEntitlementApi(@NonNull Phone phone, PersistableBundle carrierConfig) {
+        mPhone = phone;
+        mCarrierConfig = carrierConfig;
+        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.valueOf(entitlementStatus);
+                }
+                if (jsonToken.has(PROVISION_STATUS_KEY)) {
+                    provisionStatus = jsonToken.getString(PROVISION_STATUS_KEY);
+                    if (provisionStatus != null) {
+                        premiumNetworkEntitlementResponse.mProvisionStatus =
+                                Integer.valueOf(provisionStatus);
+                    }
+                }
+                if (jsonToken.has(PROVISION_TIME_LEFT_KEY)) {
+                    provisionTimeLeft = jsonToken.getString(PROVISION_TIME_LEFT_KEY);
+                    if (provisionTimeLeft != null) {
+                        premiumNetworkEntitlementResponse.mEntitlementStatus =
+                                Integer.valueOf(provisionTimeLeft);
+                    }
+                }
+                if (jsonToken.has(SERVICE_FLOW_URL_KEY)) {
+                    provisionStatus = jsonToken.getString(SERVICE_FLOW_URL_KEY);
+                    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");
+        }
+
+        return premiumNetworkEntitlementResponse;
+    }
+
+    private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
+        AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
+    }
+
+
+    /** Returns carrier config for the {@code subId}. */
+    private static PersistableBundle getConfigForSubId(Context context, int subId) {
+        CarrierConfigManager carrierConfigManager =
+                context.getSystemService(CarrierConfigManager.class);
+        PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
+        if (carrierConfig == null) {
+            Log.d(TAG, "getDefaultConfig");
+            carrierConfig = CarrierConfigManager.getDefaultConfig();
+        }
+        return carrierConfig;
+    }
+
+    /**
+     * Returns entitlement server url for the {@code subId} or
+     * a default empty string if it is not available.
+     */
+    public static String getEntitlementServerUrl(PersistableBundle carrierConfig, int subId) {
+        return carrierConfig.getString(
+                CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+                "");
+    }
+
+    private CarrierConfig getEntitlementServerCarrierConfig(PersistableBundle carrierConfig) {
+        String entitlementServiceUrl = getEntitlementServerUrl(carrierConfig, mPhone.getSubId());
+        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..4588b71
--- /dev/null
+++ b/src/com/android/phone/slice/PremiumNetworkEntitlementResponse.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+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 int mEntitlementStatus;
+    @PremiumNetworkProvisionStatus int mProvisionStatus;
+    int mProvisionTimeLeftInSeconds;
+    String mServiceFlowURL;
+
+    boolean isProvisioned() {
+        if (mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_PROVISIONED
+                || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_INCLUDED) {
+            return true;
+        }
+        return false;
+    }
+
+    boolean isProvisioningInProgress() {
+        if (mProvisionStatus == PREMIUM_NETWORK_PROVISION_STATUS_IN_PROGRESS
+                || mEntitlementStatus == PREMIUM_NETWORK_ENTITLEMENT_STATUS_PROVISIONING) {
+            return true;
+        }
+        return false;
+    }
+
+    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..0f29c70 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -16,6 +16,11 @@
 
 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 android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +33,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;
@@ -245,6 +252,8 @@
             mSlicePurchaseControllerBroadcastReceivers = new HashMap<>();
     /** The current network slicing configuration. */
     @Nullable private NetworkSlicingConfig mSlicingConfig;
+    /* Premium network entitlement query API */
+    @NonNull private PremiumNetworkEntitlementApi mPremiumNetworkEntitlementApi;
 
     private class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
         @TelephonyManager.PremiumCapability private final int mCapability;
@@ -281,7 +290,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,7 +306,7 @@
                     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;
@@ -307,7 +316,7 @@
                             + "subscription for capability: "
                             + TelephonyManager.convertPremiumCapabilityToString(capability));
                     SlicePurchaseController.getInstance(phoneId)
-                            .sendPurchaseResultFromSlicePurchaseApp(capability,
+                            .handlePurchaseResult(capability,
                             TelephonyManager
                                     .PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB,
                             false);
@@ -340,7 +349,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 +367,13 @@
         return sInstances.get(phoneId);
     }
 
-    private SlicePurchaseController(@NonNull Phone phone) {
-        super(phone.getLooper());
+    private 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,7 +471,7 @@
         }
         if (!isPremiumCapabilitySupportedByCarrier(capability)) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
                     onComplete);
             return;
         }
@@ -470,7 +483,7 @@
         }
         if (isSlicingConfigActive(capability)) {
             sendPurchaseResult(capability,
-                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+                    PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
                     onComplete);
             return;
         }
@@ -492,17 +505,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 +531,7 @@
         onComplete.sendToTarget();
     }
 
-    private void sendPurchaseResultFromSlicePurchaseApp(
+    private void handlePurchaseResult(
             @TelephonyManager.PremiumCapability int capability,
             @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle) {
         mPhone.getContext().unregisterReceiver(
@@ -569,6 +575,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);
@@ -639,7 +676,7 @@
         logd("Broadcasting timeout intent to SlicePurchaseBroadcastReceiver.");
         mPhone.getContext().sendBroadcast(intent);
 
-        sendPurchaseResultFromSlicePurchaseApp(
+        handlePurchaseResult(
                 capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, true);
     }
 
@@ -652,7 +689,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 +704,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 +730,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 +806,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/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..6ad6ebd 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,8 +19,8 @@
 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_OVERRIDDEN;
@@ -270,8 +270,8 @@
                 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_ENTITLEMENT_CHECK_FAILED:
+                return "Entitlement check failed";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB:
                 return "No default data";
             case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP: