Add implementations for service-entitlement lib

Implements the APIs for retrieving the VoWifi entitlement status through
EAP-AKA authenticate method which defines in GSMA TS.43 specification.

Bug: 173450048
Test: presubmit
Change-Id: I7549e2a0287c1294b71a83c8189109f708e5b31f
diff --git a/Android.bp b/Android.bp
index 4e110e7..42bcc38 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,9 +20,13 @@
         "java/**/*.java",
     ],
     libs: [
+        "androidx.annotation_annotation",
         "auto_value_annotations",
     ],
+    static_libs: [
+        "guava",
+    ],
     plugins: ["auto_value_plugin"],
     sdk_version: "current",
-    min_sdk_version: "30",
+    min_sdk_version: "29",
 }
diff --git a/java/com/android/libraries/entitlement/CarrierConfig.java b/java/com/android/libraries/entitlement/CarrierConfig.java
new file mode 100644
index 0000000..8f040c1
--- /dev/null
+++ b/java/com/android/libraries/entitlement/CarrierConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Carrier specific customization to be used in the service entitlement queries and operations.
+ *
+ * @see #ServiceEntitlement
+ */
+@AutoValue
+public abstract class CarrierConfig {
+    /**
+     * Returns the carrier's entitlement server URL. If not set, will use {@code
+     * https://aes.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org} as defined in GSMA spec TS.43 section 2.1.
+     */
+    public abstract String serverUrl();
+
+    /** Returns a new {@link Builder} object. */
+    public static Builder builder() {
+        return new AutoValue_CarrierConfig.Builder()
+                .setServerUrl("");
+    }
+
+    /** Builder. */
+    @AutoValue.Builder
+    public abstract static class Builder {
+        public abstract CarrierConfig build();
+
+        /**
+         * Set's the carrier's entitlement server URL. If not set, will use {@code
+         * https://aes.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org} as defined in GSMA spec TS.43 section 2.1.
+         */
+        public abstract Builder setServerUrl(String url);
+    }
+}
diff --git a/java/com/android/libraries/entitlement/EsimOdsaOperation.java b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
new file mode 100644
index 0000000..d7d23ac
--- /dev/null
+++ b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * HTTP request parameters specific to on device service actiavation (ODSA). See GSMA spec TS.43
+ * section 6.2.
+ */
+@AutoValue
+public abstract class EsimOdsaOperation {
+    /** OSDA operation: CheckEligibility. */
+    public static final String OPERATION_CHECK_ELIGIBILITY = "CheckEligibility";
+    /** OSDA operation: ManageSubscription. */
+    public static final String OPERATION_MANAGE_SUBSCRIPTION = "ManageSubscription";
+    /** OSDA operation: ManageService. */
+    public static final String OPERATION_MANAGE_SERVICE = "ManageService";
+    /** OSDA operation: AcquireConfiguration. */
+    public static final String OPERATION_ACQUIRE_CONFIGURATION = "AcquireConfiguration";
+
+    /** Indicates that operation_type is not set. */
+    static final int OPERATION_TYPE_NOT_SET = -1;
+    /** To activate a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
+    public static final int OPERATION_TYPE_SUBSCRIBE = 0;
+    /** To cancel a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
+    public static final int OPERATION_TYPE_UNSUBSCRIBE = 1;
+    /** To manage an existing subscription, for {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
+    public static final int OPERATION_TYPE_CHANGE_SUBSCRIPTION = 2;
+    /**
+     * To transfer a subscription from an existing device, used by {@link
+     * #OPERATION_MANAGE_SUBSCRIPTION}.
+     */
+    public static final int OPERATION_TYPE_TRANSFER_SUBSCRIPTION = 3;
+    /**
+     * To inform the network of a subscription update, used by
+     * {@link #OPERATION_MANAGE_SUBSCRIPTION}.
+     */
+    public static final int OPERATION_TYPE_UPDATE_SUBSCRIPTION = 4;
+    /** To activate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */
+    public static final int OPERATION_TYPE_ACTIVATE_SERVICE = 10;
+    /** To deactivate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */
+    public static final int OPERATION_TYPE_DEACTIVATE_SERVICE = 11;
+
+    /** Indicates the companion device carries the same MSISDN as the primary device. */
+    public static final String COMPANION_SERVICE_SHAERED_NUMBER = "SharedNumber";
+    /** Indicates the companion device carries a different MSISDN as the primary device. */
+    public static final String COMPANION_SERVICE_DIFFERENT_NUMBER = "DiffNumber";
+
+    /** Returns the eSIM ODSA operation. Used by HTTP parameter "operation". */
+    public abstract String operation();
+
+    /**
+     * Returns the detiled type of the eSIM ODSA operation. Used by HTTP parameter "operation_type".
+     */
+    public abstract int operationType();
+
+    /**
+     * Returns the unique identifier of the companion device, like IMEI. Used by HTTP parameter
+     * "companion_terminal_id".
+     */
+    public abstract String companionTerminalId();
+
+    /**
+     * Returns the OEM of the companion device. Used by HTTP parameter "companion_terminal_vendor".
+     */
+    public abstract String companionTerminalVendor();
+
+    /**
+     * Returns the model of the companion device. Used by HTTP parameter "companion_terminal_model".
+     */
+    public abstract String companionTerminalModel();
+
+    /**
+     * Returns the software version of the companion device. Used by HTTP parameter
+     * "companion_terminal_sw_version".
+     */
+    public abstract String companionTerminalSoftwareVersion();
+
+    /**
+     * Returns the user-friendly version of the companion device. Used by HTTP parameter
+     * "companion_terminal_friendly_name".
+     */
+    public abstract String companionTerminalFriendlyName();
+
+    /**
+     * Returns the service type of the companion device, e.g. if the MSISDN is same as the primary
+     * device. Used by HTTP parameter "companion_terminal_service".
+     */
+    public abstract String companionTerminalService();
+
+    /**
+     * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid".
+     */
+    public abstract String companionTerminalIccid();
+
+    /**
+     * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid".
+     */
+    public abstract String companionTerminalEid();
+
+    /** Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid". */
+    public abstract String terminalIccid();
+
+    /**
+     * Returns the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
+     * "terminal_eid".
+     */
+    public abstract String terminalEid();
+
+    /**
+     * Returns the unique identifier of the primary device eSIM, like the IMEI associated with the
+     * eSIM. Used by HTTP parameter "target_terminal_id".
+     */
+    public abstract String targetTerminalId();
+
+    /** Returns the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid". */
+    public abstract String targetTerminalIccid();
+
+    /**
+     * Returns the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
+     * "target_terminal_eid".
+     */
+    public abstract String targetTerminalEid();
+
+    /** Returns a new {@link Builder} object. */
+    public static Builder builder() {
+        return new AutoValue_EsimOdsaOperation.Builder().setOperationType(OPERATION_TYPE_NOT_SET);
+    }
+
+    /**
+     * Builder.
+     *
+     * <p>For ODSA, the rule of which parameters are required varies or each
+     * operation/opeation_type.
+     * The Javadoc below gives high-level description, but please refer to GMSA spec TS.43 section
+     * 6.2
+     * for details.
+     */
+    @AutoValue.Builder
+    public abstract static class Builder {
+        /**
+         * Sets the eSIM ODSA operation. Used by HTTP parameter "operation".
+         *
+         * <p>Required.
+         *
+         * @see #OPERATION_CHECK_ELIGIBILITY
+         * @see #OPERATION_MANAGE_SUBSCRIPTION
+         * @see #OPERATION_MANAGE_SERVICE
+         * @see #OPERATION_ACQUIRE_CONFIGURATION
+         */
+        public abstract Builder setOperation(String value);
+
+        /**
+         * Sets the detiled type of the eSIM ODSA operation. Used by HTTP parameter "operation_type"
+         * if set.
+         *
+         * <p>Required by some operation.
+         *
+         * @see #OPERATION_TYPE_SUBSCRIBE
+         * @see #OPERATION_TYPE_UNSUBSCRIBE
+         * @see #OPERATION_TYPE_CHANGE_SUBSCRIPTION
+         * @see #OPERATION_TYPE_TRANSFER_SUBSCRIPTION
+         * @see #OPERATION_TYPE_UPDATE_SUBSCRIPTION
+         * @see #OPERATION_TYPE_ACTIVATE_SERVICE
+         * @see #OPERATION_TYPE_DEACTIVATE_SERVICE
+         */
+        public abstract Builder setOperationType(int value);
+
+        /**
+         * Sets the unique identifier of the companion device, like IMEI. Used by HTTP parameter
+         * "companion_terminal_id" if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalId(String value);
+
+        /**
+         * Sets the OEM of the companion device. Used by HTTP parameter "companion_terminal_vendor"
+         * if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalVendor(String value);
+
+        /**
+         * Sets the model of the companion device. Used by HTTP parameter "companion_terminal_model"
+         * if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalModel(String value);
+
+        /**
+         * Sets the software version of the companion device. Used by HTTP parameter
+         * "companion_terminal_sw_version" if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalSoftwareVersion(String value);
+
+        /**
+         * Sets the user-friendly version of the companion device. Used by HTTP parameter
+         * "companion_terminal_friendly_name" if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalFriendlyName(String value);
+
+        /**
+         * Sets the service type of the companion device, e.g. if the MSISDN is same as the primary
+         * device. Used by HTTP parameter "companion_terminal_service" if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         *
+         * @see #COMPANION_SERVICE_SHAERED_NUMBER
+         * @see #COMPANION_SERVICE_DIFFERENT_NUMBER
+         */
+        public abstract Builder setCompanionTerminalService(String value);
+
+        /**
+         * Sets the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid"
+         * if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalIccid(String value);
+
+        /**
+         * Sets the eUICC identifier (EID) of the companion device. Used by HTTP parameter
+         * "companion_terminal_eid" if set.
+         *
+         * <p>Used by companion device ODSA operation.
+         */
+        public abstract Builder setCompanionTerminalEid(String value);
+
+        /**
+         * Sets the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid" if set.
+         *
+         * <p>Used by primary device ODSA operation.
+         */
+        public abstract Builder setTerminalIccid(String value);
+
+        /**
+         * Sets the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
+         * "terminal_eid" if set.
+         *
+         * <p>Used by primary device ODSA operation.
+         */
+        public abstract Builder setTerminalEid(String value);
+
+        /**
+         * Sets the unique identifier of the primary device eSIM, like the IMEI associated with the
+         * eSIM. Used by HTTP parameter "target_terminal_id" if set.
+         *
+         * <p>Used by primary device ODSA operation.
+         */
+        public abstract Builder setTargetTerminalId(String value);
+
+        /**
+         * Sets the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid" if
+         * set.
+         *
+         * <p>Used by primary device ODSA operation.
+         */
+        public abstract Builder setTargetTerminalIccid(String value);
+
+        /**
+         * Sets the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
+         * "target_terminal_eid" if set.
+         *
+         * <p>Used by primary device ODSA operation.
+         */
+        public abstract Builder setTargetTerminalEid(String value);
+
+        public abstract EsimOdsaOperation build();
+    }
+}
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java
new file mode 100644
index 0000000..c196783
--- /dev/null
+++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement;
+
+import android.content.Context;
+
+import com.android.libraries.entitlement.eapaka.EapAkaApi;
+
+import java.util.List;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Implemnets protocol for carrier service entitlement configuration query and operation, based on
+ * GSMA TS.43 spec.
+ */
+public class ServiceEntitlement {
+    /** App ID for Voice-Over-LTE entitlement. */
+    public static final String APP_VOLTE = "ap2003";
+    /** App ID for Voice-Over-WiFi entitlement. */
+    public static final String APP_VOWIFI = "ap2004";
+    /** App ID for SMS-Over-IP entitlement. */
+    public static final String APP_SMSOIP = "ap2005";
+    /** App ID for on device service activation (OSDA) for companion device. */
+    public static final String APP_ODSA_COMPANION = "ap2006";
+    /** App ID for on device service activation (OSDA) for primary device. */
+    public static final String APP_ODSA_PRIMARY = "ap2009";
+
+    private final Context context;
+    private final int simSubscriptionId;
+    private final CarrierConfig carrierConfig;
+
+    /**
+     * Creates an instance for service entitlement configuration query and operation for the
+     * carrier.
+     *
+     * @param context           context of application
+     * @param carrierConfig     carrier specific configs used in the queries and operations.
+     * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates
+     *                          which SIM to retrieve IMEI/IMSI from and perform EAP-AKA
+     *                          authentication with. See {@link
+     *                          android.telephony.SubscriptionManager} for how to get the
+     *                          subscroption ID.
+     */
+    public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) {
+        this.context = context;
+        this.simSubscriptionId = simSubscriptionId;
+        this.carrierConfig = carrierConfig;
+    }
+
+    /**
+     * Retrieves service entitlement configuration. For on device service activation (ODSA) of eSIM
+     * for companion/primary devices, use {@link #performEsimOdsa} instead.
+     *
+     * <p>Supported {@code appId}: {@link #APP_VOLTE}, {@link #APP_VOWIFI}, {@link #APP_SMSOIP}.
+     *
+     * <p>This method sends an HTTP GET request to entitlement server, responds to EAP-AKA
+     * challenge
+     * if needed, and returns the raw configuration doc as a string. The following parameters are
+     * set
+     * in the HTTP request:
+     *
+     * <ul>
+     * <li>"app": {@code appId}
+     * <li>"vers": 0, or {@code request.configurationVersion()} if it's not 0.
+     * <li>"entitlement_version": "2.0", or {@code request.entitlementVersion()} if it's not empty.
+     * <li>"token": not set, or {@code request.authenticationToken()} if it's not empty.
+     * <li>"IMSI": if "token" is set, set to {@link android.telephony.TelephonyManager#getImei}.
+     * <li>"EAP_ID": if "token" is not set, set this parameter to trigger embedded EAP-AKA
+     * authentication as decribed in TS.43 section 2.6.1. Its value is derived from IMSI as per
+     * GSMA spec RCC.14 section C.2.
+     * <li>"terminal_id": IMEI, or {@code request.terminalId()} if it's not empty.
+     * <li>"terminal_vendor": {@link android.os.Build#MANUFACTURER}, or {@code
+     * request.terminalVendor()} if it's not empty.
+     * <li>"terminal_model": {@link android.os.Build#MODEL}, or {@code request.terminalModel()} if
+     * it's not empty.
+     * <li>"terminal_sw_version": {@llink android.os.Build.VERSION#BASE_OS}, or {@code
+     * request.terminalSoftwareVersion()} if it's not empty.
+     * <li>"app_name": not set, or {@code request.appName()} if it's not empty.
+     * <li>"app_version": not set, or {@code request.appVersion()} if it's not empty.
+     * <li>"notif_token": not set, or {@code request.notificationToken()} if it's not empty.
+     * <li>"notif_action": {@code request.notificationAction()} if "notif_token" is set, otherwise
+     * not set.
+     * </ul>
+     *
+     * <p>Requires permission: READ_PRIVILEGED_PHONE_STATE, or carrier privilege.
+     *
+     * @param appId   an app ID string defined in TS.43 section 2.2, e.g. {@link #APP_VOWIFI}.
+     * @param request contains parameters that can be used in the HTTP request.
+     */
+    @Nullable
+    public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request)
+            throws ServiceEntitlementException {
+        EapAkaApi eapAkaApi = new EapAkaApi(context, simSubscriptionId);
+        return eapAkaApi.queryEntitlementStatus(appId, carrierConfig.serverUrl(), request);
+    }
+
+    /**
+     * Retrieves service entitlement configurations for multiple app IDs in one HTTP
+     * request/response.
+     * For on device service activation (ODSA) of eSIM for companion/primary devices, use {@link
+     * #performEsimOdsa} instead.
+     *
+     * <p>Same with {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that
+     * multiple "app" parameters will be set in the HTTP request, in the order as they appear in
+     * parameter {@code appIds}.
+     */
+    public String queryEntitlementStatus(List<String> appIds, ServiceEntitlementRequest request)
+            throws ServiceEntitlementException {
+        // TODO(b/177544547): Add implementation
+        return null;
+    }
+
+    /**
+     * Performs on device service activation (ODSA) of eSIM for companion/primary devices.
+     *
+     * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}.
+     *
+     * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this method
+     * sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if needed, and
+     * returns the raw configuration doc as a string. Additional parameters from {@code operation}
+     * are set to the HTTP request. See {@link EsimOdsaOperation} for details.
+     */
+    public String performEsimOdsa(
+            String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)
+            throws ServiceEntitlementException {
+        // TODO(b/177544547): Add implementation
+        return null;
+    }
+}
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementException.java b/java/com/android/libraries/entitlement/ServiceEntitlementException.java
new file mode 100644
index 0000000..499a032
--- /dev/null
+++ b/java/com/android/libraries/entitlement/ServiceEntitlementException.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement;
+
+/** Indicates errors happened in retrieving service entitlement configuration. */
+public class ServiceEntitlementException extends Exception {
+    /** Unknown error. */
+    public static final int ERROR_UNKNOWN = 0;
+    /** Android telephony is unable to provide info like IMSI, e.g. when modem crashed. */
+    public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+    /**
+     * SIM not returning a response to the EAP-AKA challenge, e.g. when the challenge is invalid.
+     * This
+     * can happen only when an embedded EAP-AKA challange is conducted, as per GMSA spec TS.43
+     * section
+     * 2.6.1.
+     */
+    public static final int ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE = 2;
+    /**
+     * Cannot connect to the entitlment server, e.g. due to weak mobile network and Wi-Fi
+     * connection.
+     */
+    public static final int ERROR_SEVER_NOT_CONNECTABLE = 3;
+    /**
+     * HTTP response received with a status code indicating failure, e.g. 4xx and 5xx. Use {@link
+     * #getHttpStatus} to get the status code and {@link #getMessage} the error message in the
+     * response body.
+     */
+    public static final int ERROR_HTTP_STATUS_NOT_SUCCESS = 4;
+
+    public ServiceEntitlementException(String message) {
+        // TODO(b/177544547): add implementation
+    }
+
+    public ServiceEntitlementException(
+            int error, int httpStatus, String retryAfter, String message, Throwable cause) {
+        // TODO(b/177544547): add implementation
+    }
+
+    /** Returns the error code, see {@link #ERROR_*}. */
+    public int getErrorCode() {
+        // TODO(b/177544547): add implementation
+        return ERROR_UNKNOWN;
+    }
+
+    /** Returns the HTTP status code returned by entitlement server; 0 if unavailable. */
+    public int getHttpStatus() {
+        // TODO(b/177544547): add implementation
+        return ERROR_SEVER_NOT_CONNECTABLE;
+    }
+
+    /**
+     * Returns the "Retry-After" header in HTTP response, often set with HTTP status code 503; an
+     * empty string if unavailable.
+     *
+     * @return the HTTP-date or a number of seconds to delay, as defiend in RFC 7231:
+     * https://tools.ietf.org/html/rfc7231#section-7.1.3
+     */
+    public String getRetryAfter() {
+        // TODO(b/177544547): add implementation
+        return null;
+    }
+}
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java
new file mode 100644
index 0000000..6bbfee6
--- /dev/null
+++ b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement;
+
+import android.os.Build;
+import android.os.Build.VERSION;
+
+import com.google.auto.value.AutoValue;
+
+/** Service entitlement HTTP request parameters, as defiend in GSMA spec TS.43 section 2.2. */
+@AutoValue
+public abstract class ServiceEntitlementRequest {
+    /** Disables notification token. */
+    public static final int NOTICATION_ACTION_DISABLE = 0;
+    /** Enables FCM notification token. */
+    public static final int NOTICATION_ACTION_ENABLE_FCM = 2;
+
+    /**
+     * Returns the version of configuration currently stored on the client. Used by HTTP parameter
+     * "vers".
+     */
+    public abstract int configurationVersion();
+
+    /**
+     * Returns the version of the entitlement specification. Used by HTTP parameter
+     * "entitlement_version".
+     */
+    public abstract String entitlementVersion();
+
+    /** Returns the authentication token. Used by HTTP parameter "token". */
+    public abstract String authenticationToken();
+
+    /**
+     * Returns the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id".
+     */
+    public abstract String terminalId();
+
+    /** Returns the OEM of the device. Used by HTTP parameter "terminal_vendor". */
+    public abstract String terminalVendor();
+
+    /** Returns the model of the device. Used by HTTP parameter "terminal_model". */
+    public abstract String terminalModel();
+
+    /** Returns the software version of the device. Used by HTTP parameter "terminal_sw_version". */
+    public abstract String terminalSoftwareVersion();
+
+    /**
+     * Returns the name of the device application making the request. Used by HTTP parameter
+     * "app_name".
+     */
+    public abstract String appName();
+
+    /**
+     * Returns the version of the device application making the request. Used by HTTP parameter
+     * "app_version".
+     */
+    public abstract String appVersion();
+
+    /**
+     * Returns the FCM registration token used to register for entitlement configuration request
+     * from
+     * network. Used by HTTP parameter "notif_token".
+     */
+    public abstract String notificationToken();
+
+    /**
+     * Returns the action associated with the FCM registration token. Used by HTTP parameter
+     * "notif_action".
+     *
+     * @see #NOTICATION_ACTION_ENABLE_FCM
+     * @see #NOTICATION_ACTION_DISABLE
+     */
+    public abstract int notificationAction();
+
+    /** Returns a new {@link Builder} object. */
+    public static Builder builder() {
+        return new AutoValue_ServiceEntitlementRequest.Builder()
+                .setConfigurationVersion(0)
+                .setEntitlementVersion("2.0")
+                .setAuthenticationToken("")
+                .setTerminalId("")
+                .setTerminalVendor(Build.MANUFACTURER)
+                .setTerminalModel(Build.MODEL)
+                .setTerminalSoftwareVersion(VERSION.BASE_OS)
+                .setAppName("")
+                .setAppVersion("")
+                .setNotificationToken("")
+                .setNotificationAction(NOTICATION_ACTION_ENABLE_FCM);
+    }
+
+    /** Builder. */
+    @AutoValue.Builder
+    public abstract static class Builder {
+        /**
+         * Sets the version of configuration currently stored on the client. Used by HTTP parameter
+         * "vers".
+         *
+         * <p>If not set, default to 0 indicating no existing configuration.
+         */
+        public abstract Builder setConfigurationVersion(int value);
+
+        /**
+         * Sets the current version of the entitlement specification. Used by HTTP parameter
+         * "entitlement_version".
+         *
+         * <p>If not set, default to "2.0" base on TS.43-v5.0.
+         */
+        public abstract Builder setEntitlementVersion(String value);
+
+        /**
+         * Sets the authentication token. Used by HTTP parameter "token".
+         *
+         * <p>If not set, will trigger embedded EAP-AKA authentication as decribed in TS.43 section
+         * 2.6.1.
+         */
+        public abstract Builder setAuthenticationToken(String value);
+
+        /**
+         * Sets the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id".
+         *
+         * <p>If not set, will use the device IMEI.
+         */
+        public abstract Builder setTerminalId(String value);
+
+        /**
+         * Sets the OEM of the device. Used by HTTP parameter "terminal_vendor".
+         *
+         * <p>If not set, will use {@link android.os.Build#MANUFACTURER}.
+         */
+        public abstract Builder setTerminalVendor(String value);
+
+        /**
+         * Sets the model of the device. Used by HTTP parameter "terminal_model".
+         *
+         * <p>If not set, will use {@link android.os.Build#MODEL}.
+         */
+        public abstract Builder setTerminalModel(String value);
+
+        /**
+         * Sets the software version of the device. Used by HTTP parameter "terminal_sw_version".
+         *
+         * <p>If not set, will use {@link android.os.Build.VERSION#BASE_OS}.
+         */
+        public abstract Builder setTerminalSoftwareVersion(String value);
+
+        /**
+         * Sets the name of the device application making the request. Used by HTTP parameter
+         * "app_name".
+         *
+         * <p>Optional.
+         */
+        public abstract Builder setAppName(String value);
+
+        /**
+         * Sets the version of the device application making the request. Used by HTTP parameter
+         * "app_version".
+         *
+         * <p>Optional.
+         */
+        public abstract Builder setAppVersion(String value);
+
+        /**
+         * Sets the FCM registration token used to register for entitlement configuration request
+         * from
+         * network. Used by HTTP parameter "notif_token".
+         *
+         * <p>Optional.
+         */
+        public abstract Builder setNotificationToken(String value);
+
+        /**
+         * Sets the action associated with the FCM registration token. Used by HTTP parameter
+         * "notif_action".
+         *
+         * <p>Required if a token is set with {@link #setNotificationToken}, and default to {@link
+         * #NOTICATION_ACTION_ENABLE_FCM}; otherwise ignored.
+         *
+         * @see #NOTICATION_ACTION_ENABLE_FCM
+         * @see #NOTICATION_ACTION_DISABLE
+         */
+        public abstract Builder setNotificationAction(int value);
+
+        public abstract ServiceEntitlementRequest build();
+    }
+}
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
new file mode 100644
index 0000000..898b782
--- /dev/null
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
+import com.android.libraries.entitlement.http.HttpClient;
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
+import com.android.libraries.entitlement.http.HttpConstants.RequestMethod;
+import com.android.libraries.entitlement.http.HttpRequest;
+import com.android.libraries.entitlement.http.HttpResponse;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.net.HttpHeaders;
+
+import java.net.CookieHandler;
+import java.net.CookieManager;
+
+public class EapAkaApi {
+    private static final String TAG = "ServiceEntitlement";
+
+    public static final String EAP_CHALLENGE_RESPONSE = "eap-relay-packet";
+
+    /** Current version of the entitlement configuration. */
+    private static final String VERS = "vers";
+    /** Version of the entitlement configuration. */
+    private static final String ENTITLEMENT_VERSION = "entitlement_version";
+    /**
+     * Unique identifier for the device. Refer to {@link
+     * android.telephony.TelephonyManager#getImei()}.
+     */
+    private static final String TERMINAL_ID = "terminal_id";
+    /** Device manufacturer. */
+    private static final String TERMINAL_VENDOR = "terminal_vendor";
+    /** Device model. */
+    private static final String TERMINAL_MODEL = "terminal_model";
+    /** Device software version. */
+    private static final String TERMIAL_SW_VERSION = "terminal_sw_version";
+    /** Identifier for the requested entitlement. */
+    private static final String APP = "app";
+    /** NAI needed for EAP-AKA authentication. */
+    private static final String EAP_ID = "EAP_ID";
+
+    private static final String IMSI = "IMSI";
+    private static final String TOKEN = "token";
+    /** Action for the notification registration token. */
+    private static final String NOTIF_ACTION = "notif_action";
+    /** Attribute name of the notification registration token. */
+    private static final String NOTIF_TOKEN = "notif_token";
+    /** Attribute name of the app version. */
+    private static final String APP_VERSION = "app_version";
+    /** Attribute name of the app name. */
+    private static final String APP_NAME = "app_name";
+
+    private final Context context;
+    private final int simSubscriptionId;
+    private final HttpClient httpClient;
+
+    public EapAkaApi(Context context, int simSubscriptionId) {
+        this.context = context;
+        this.simSubscriptionId = simSubscriptionId;
+        this.httpClient = new HttpClient();
+    }
+
+    /**
+     * Retrieves raw entitlement configuration doc though EAP-AKA authentication.
+     *
+     * <p>Implementation based on GSMA TS.43-v5.0 2.6.1.
+     *
+     * @throws ServiceEntitlementException when getting an unexpected http response.
+     */
+    @Nullable
+    public String queryEntitlementStatus(
+            String appId, String serverUrl, ServiceEntitlementRequest request)
+            throws ServiceEntitlementException {
+        // TODO(b/177562073): localize cookie management instead of VM global CookieHandler
+        CookieHandler.setDefault(new CookieManager());
+
+        HttpRequest httpRequest =
+                HttpRequest.builder()
+                        .setUrl(entitlementStatusUrl(appId, serverUrl, request))
+                        .setRequestMethod(RequestMethod.GET)
+                        .addRequestProperty(
+                                HttpHeaders.ACCEPT,
+                                "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap"
+                                        + ".connectivity-xml")
+                        .build();
+        HttpResponse response = httpClient.request(httpRequest);
+        if (response == null) {
+            throw new ServiceEntitlementException("Null http response");
+        }
+        if (response.contentType() == ContentType.JSON) {
+            try {
+                // EapAka token challenge for initial AuthN
+                Log.d(TAG, "initial AuthN");
+                String akaChallengeResponse =
+                        new EapAkaResponse(
+                                new JSONObject(response.body()).getString(EAP_CHALLENGE_RESPONSE))
+                                .getEapAkaChallengeResponse(context, simSubscriptionId);
+                JSONObject postData = new JSONObject();
+                postData.put(EAP_CHALLENGE_RESPONSE, akaChallengeResponse);
+                return challengeResponse(postData, serverUrl);
+            } catch (JSONException jsonException) {
+                Log.e(TAG, "queryEntitlementStatus failed. jsonException: " + jsonException);
+                return null;
+            }
+        } else if (response.contentType() == ContentType.XML) {
+            // Result of fast AuthN
+            Log.d(TAG, "fast AuthN");
+            return response.body();
+        }
+        throw new ServiceEntitlementException("Unexpected http ContentType");
+    }
+
+    private String challengeResponse(JSONObject postData, String serverUrl)
+            throws ServiceEntitlementException {
+        Log.d(TAG, "challengeResponse");
+        HttpRequest request =
+                HttpRequest.builder()
+                        .setUrl(serverUrl)
+                        .setRequestMethod(RequestMethod.POST)
+                        .setPostData(postData)
+                        .addRequestProperty(
+                                HttpHeaders.ACCEPT,
+                                "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap"
+                                        + ".connectivity-xml")
+                        .addRequestProperty(HttpHeaders.CONTENT_TYPE,
+                                "application/vnd.gsma.eap-relay.v1.0+json")
+                        .build();
+
+        HttpResponse response = httpClient.request(request);
+        if (response == null || response.contentType() != ContentType.XML) {
+            throw new ServiceEntitlementException("Unexpected http response.");
+        }
+
+        return response.body();
+    }
+
+    private String entitlementStatusUrl(
+            String appId, String serverUrl, ServiceEntitlementRequest request) {
+        TelephonyManager telephonyManager =
+                context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+                        simSubscriptionId);
+        Uri.Builder urlBuilder = Uri.parse(serverUrl).buildUpon();
+        if (TextUtils.isEmpty(request.authenticationToken())) {
+            // EAP_ID required for initial AuthN
+            urlBuilder.appendQueryParameter(
+                    EAP_ID,
+                    getImsiEap(telephonyManager.getSimOperator(),
+                               telephonyManager.getSubscriberId()));
+        } else {
+            // IMSI and token required for fast AuthN.
+            urlBuilder
+                    .appendQueryParameter(IMSI, telephonyManager.getSubscriberId())
+                    .appendQueryParameter(TOKEN, request.authenticationToken());
+        }
+
+        if (!TextUtils.isEmpty(request.notificationToken())) {
+            urlBuilder
+                    .appendQueryParameter(NOTIF_ACTION,
+                            Integer.toString(request.notificationAction()))
+                    .appendQueryParameter(NOTIF_TOKEN, request.notificationToken());
+        }
+
+        // Assign terminal ID with device IMEI if not set.
+        if (TextUtils.isEmpty(request.terminalId())) {
+            urlBuilder.appendQueryParameter(TERMINAL_ID, telephonyManager.getImei());
+        } else {
+            urlBuilder.appendQueryParameter(TERMINAL_ID, request.terminalId());
+        }
+
+        // Optional query parameters, append them if not empty
+        if (!TextUtils.isEmpty(request.appVersion())) {
+            urlBuilder.appendQueryParameter(APP_VERSION, request.appVersion());
+        }
+
+        if (!TextUtils.isEmpty(request.appName())) {
+            urlBuilder.appendQueryParameter(APP_NAME, request.appName());
+        }
+
+        return urlBuilder
+                // Identity and Authentication parameters
+                .appendQueryParameter(TERMINAL_VENDOR, request.terminalVendor())
+                .appendQueryParameter(TERMINAL_MODEL, request.terminalModel())
+                .appendQueryParameter(TERMIAL_SW_VERSION, request.terminalSoftwareVersion())
+                // General Service parameters
+                .appendQueryParameter(APP, appId)
+                .appendQueryParameter(VERS, Integer.toString(request.configurationVersion()))
+                .appendQueryParameter(ENTITLEMENT_VERSION, request.entitlementVersion())
+                .toString();
+    }
+
+    /**
+     * Returns the IMSI EAP value. The resulting realm part of the Root NAI in 3GPP TS 23.003 clause
+     * 19.3.2 will be in the form:
+     *
+     * <p>{@code 0<IMSI>@nai.epc.mnc<MNC>.mcc<MCC>.3gppnetwork.org}
+     */
+    @Nullable
+    static String getImsiEap(@Nullable String mccmnc, @Nullable String imsi) {
+        if (mccmnc == null || mccmnc.length() < 5 || imsi == null) {
+            return null;
+        }
+
+        String mcc = mccmnc.substring(0, 3);
+        String mnc = mccmnc.substring(3);
+        if (mnc.length() == 2) {
+            mnc = "0" + mnc;
+        }
+        return "0" + imsi + "@nai.epc.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org";
+    }
+}
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java
new file mode 100644
index 0000000..5021317
--- /dev/null
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.eapaka.utils.BytesConverter;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import androidx.annotation.Nullable;
+
+/** Generate the response of EAP-AKA token challenge for initial AUTN. */
+class EapAkaResponse {
+    private static final String TAG = "ServiceEntitlement";
+
+    private static final int EAP_AKA_HEADER_LENGTH = 8;
+    private static final byte CODE_REQUEST = 0x01;
+    private static final byte CODE_RESPONSE = 0x02;
+    private static final byte TYPE_EAP_AKA = 0x17;
+    private static final byte SUBTYPE_AKA_CHALLENGE = 0x01;
+    private static final byte ATTRIBUTE_RAND = 0x01;
+    private static final byte ATTRIBUTE_AUTN = 0x02;
+    private static final byte ATTRIBUTE_RES = 0x03;
+    private static final byte ATTRIBUTE_MAC = 0x0B;
+    private static final String ALGORITHM_HMAC_SHA1 = "HmacSHA1";
+    private static final int RAND_LENGTH = 20;
+    private static final int AUTN_LENGTH = 20;
+    private static final int SHA1_OUTPUT_LENGTH = 20;
+
+    /** RAND length 16. */
+    private static final byte RAND_LEN = 0x10;
+    /** AUTN length 16. */
+    private static final byte AUTN_LEN = 0x10;
+
+    /* 1 for Request, 2 for Response*/
+    private byte code = -1;
+    /* The identifier of Response must same as Request */
+    private byte identifier = -1;
+    /* The total length of full EAP-AKA message, include code, identifier, ... */
+    private int length = -1;
+    /* In EAP-AKA, the Type field is set to 23 */
+    private byte type = -1;
+    /* SubType for AKA-Challenge should be 1 */
+    private byte subType = -1;
+    /* The value of AT_AUTN, network authentication token */
+    private byte[] autn;
+    /* The value of AT_RAND, RAND random number*/
+    private byte[] rand;
+
+    private boolean valid;
+
+    public EapAkaResponse(String eapAkaChallenge) {
+        try {
+            parseEapAkaChallengeRequest(eapAkaChallenge);
+        } catch (Exception e) {
+            Log.e(TAG, "parseEapAkaChallengeRequest Exception:", e);
+            valid = false;
+        }
+    }
+
+    /** Refer to RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format. */
+    private void parseEapAkaChallengeRequest(String request) {
+        if (TextUtils.isEmpty(request)) {
+            return;
+        }
+
+        try {
+            byte[] data = Base64.decode(request, Base64.DEFAULT);
+            if (parseEapAkaHeader(data) && parseRandAndAutn(data)) {
+                valid = true;
+            } else {
+                Log.d(TAG, "Invalid data!");
+            }
+        } catch (IllegalArgumentException illegalArgumentException) {
+            Log.e(TAG, "Invalid base-64 content");
+        }
+    }
+
+    /**
+     * Parse EAP-AKA header, 8 bytes include 2 reserved bytes.
+     *
+     * @param data raw bytes of request data.
+     * @return {@code true} if success to parse the header of request data.
+     */
+    private boolean parseEapAkaHeader(byte[] data) {
+        if (data.length <= EAP_AKA_HEADER_LENGTH) {
+            return false;
+        }
+        code = data[0];
+        identifier = data[1];
+        length = ((data[2] & 0xff) << 8) | (data[3] & 0xff);
+        type = data[4];
+        subType = data[5];
+
+        // valid header
+        if (code != CODE_REQUEST
+                || length != data.length
+                || type != TYPE_EAP_AKA
+                || subType != SUBTYPE_AKA_CHALLENGE) {
+            Log.d(
+                    TAG,
+                    "Invalid EAP-AKA Header, code="
+                            + code
+                            + ", length="
+                            + length
+                            + ", real length="
+                            + data.length
+                            + ", type="
+                            + type
+                            + ", subType="
+                            + subType);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Refer to RFC 4187 section 10.6 AT_RAND/RFC 4187 section 10.7 AT_AUTN.
+     *
+     * @param data raw bytes of request data.
+     * @return {@code true} if success to parse the RAND and AUTN data.
+     */
+    private boolean parseRandAndAutn(byte[] data) {
+        int index = EAP_AKA_HEADER_LENGTH;
+        while (index < data.length) {
+            int remainsLength = data.length - index;
+            if (remainsLength <= 2) {
+                Log.d(TAG, "Error! remainsLength = " + remainsLength);
+                return false;
+            }
+
+            byte attributeType = data[index];
+
+            // the length of this attribute in multiples of 4 bytes, include attribute type and
+            // length
+            int length = (data[index + 1] & 0xff) * 4;
+            if (length > remainsLength) {
+                Log.d(TAG,
+                        "Length Error! length is " + length + " but only remains " + remainsLength);
+                return false;
+            }
+
+            // see RFC 4187 section 11 for attribute type
+            if (attributeType == ATTRIBUTE_RAND) {
+                if (length != RAND_LENGTH) {
+                    Log.d(TAG, "AT_RAND length is " + length);
+                    return false;
+                }
+                rand = new byte[16];
+                System.arraycopy(data, index + 4, rand, 0, 16);
+            } else if (attributeType == ATTRIBUTE_AUTN) {
+                if (length != AUTN_LENGTH) {
+                    Log.d(TAG, "AT_AUTN length is " + length);
+                    return false;
+                }
+                autn = new byte[16];
+                System.arraycopy(data, index + 4, autn, 0, 16);
+            }
+
+            index += length;
+        } // while
+
+        // check has AT_RAND and AT_AUTH
+        if (rand == null || autn == null) {
+            Log.d(TAG, "Invalid Type Datas!");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns EAP-AKA challenge response message which generated with SIM EAP-AKA authentication
+     * with
+     * network provided EAP-AKA challenge request message.
+     */
+    public String getEapAkaChallengeResponse(Context context, int simSubscriptionId)
+            throws ServiceEntitlementException {
+        if (!valid) {
+            throw new ServiceEntitlementException("EAP-AKA Challenge message not valid!");
+        }
+
+        TelephonyManager telephonyManager =
+                context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+                        simSubscriptionId);
+
+        // process EAP-AKA authentication with SIM
+        String response =
+                telephonyManager.getIccAuthentication(
+                        TelephonyManager.APPTYPE_USIM,
+                        TelephonyManager.AUTHTYPE_EAP_AKA,
+                        getSimAuthChallengeData());
+
+        EapAkaSecurityContext securityContext = EapAkaSecurityContext.from(response);
+        // RFC 4187, section 7.  Key Generation
+        // generate master key
+        MasterKey mk =
+                MasterKey.create(
+                        EapAkaApi.getImsiEap(telephonyManager.getSubscriberId(),
+                                telephonyManager.getSimOperator()),
+                        securityContext.getIk(),
+                        securityContext.getCk());
+        // K_aut is the key used to calculate MAC
+        if (mk.getAut() == null) {
+            throw new ServiceEntitlementException("Can't generate K_Aut!");
+        }
+
+        // generate EAP-AKA Challenge Response message
+        byte[] challengeResponse =
+                generateEapAkaChallengeResponse(securityContext.getRes(), mk.getAut());
+        if (challengeResponse == null) {
+            throw new ServiceEntitlementException(
+                    "Failed to generate EAP-AKA Challenge Response data!");
+        }
+
+        return Base64.encodeToString(challengeResponse, Base64.NO_WRAP).trim();
+    }
+
+    /** Returns Base64 encoded GSM/3G security context for SIM Authentication request. */
+    @Nullable
+    private String getSimAuthChallengeData() {
+        if (!valid) {
+            return null;
+        }
+
+        byte[] challengeData = new byte[RAND_LEN + AUTN_LEN + 2];
+        challengeData[0] = RAND_LEN;
+        System.arraycopy(rand, 0, challengeData, 1, RAND_LEN);
+        challengeData[RAND_LEN + 1] = AUTN_LEN;
+        System.arraycopy(autn, 0, challengeData, RAND_LEN + 2, AUTN_LEN);
+
+        return Base64.encodeToString(challengeData, Base64.NO_WRAP).trim();
+    }
+
+    /** Returns EAP-AKA Challenge response message byte array data or null if failed to generate. */
+    @Nullable
+    public byte[] generateEapAkaChallengeResponse(@Nullable byte[] res, byte[] aut) {
+        if (res == null || aut == null) {
+            return null;
+        }
+
+        byte[] message = createEapAkaChallengeResponse(res);
+
+        // use K_aut as key to calculate mac
+        byte[] mac = calculateMac(aut, message);
+        if (mac == null) {
+            return null;
+        }
+
+        // fill MAC value to the message
+        // The value start index is 8 + AT_RES (4 + res.length) + header of AT_MAC (4)
+        int index = 8 + 4 + res.length + 4;
+        System.arraycopy(mac, 0, message, index, mac.length);
+
+        return message;
+    }
+
+    // AT_MAC/AT_RES are must included in response message
+    //
+    // Reference RFC 4187 Section 8.1 Message Format
+    //           RFC 4187 Section 9.4 EAP-Response/AKA-Challenge
+    //           RFC 3748, Section 4.1.  Request and Response
+    private byte[] createEapAkaChallengeResponse(byte[] res) {
+        // size = 8 (header) + resHeader (4) + res.length + AT_MAC (20 bytes)
+        byte[] message = new byte[32 + res.length];
+
+        // set up header
+        message[0] = CODE_RESPONSE;
+        // Identifier need to same with request
+        message[1] = identifier;
+        // length include entire EAP-AKA message
+        byte[] lengthBytes = BytesConverter.convertIntegerTo4Bytes(message.length);
+        message[2] = lengthBytes[2];
+        message[3] = lengthBytes[3];
+        message[4] = TYPE_EAP_AKA;
+        message[5] = SUBTYPE_AKA_CHALLENGE;
+        // Reserved 2 bytes
+        message[6] = 0x00;
+        message[7] = 0x00;
+
+        int index = 8;
+
+        // set up AT_RES, RFC 4187, Section 10.8 AT_RES
+        message[index++] = ATTRIBUTE_RES;
+        // The length of the AT_RES attribute must be a multiple of 4 bytes which identifies the
+        // exact length of the RES in bits. To pad 4 onto the length to ensure the reserved buffer
+        // size large enough after convert to byte count.
+        int resLength = (res.length + 4) / 4;
+        message[index++] = (byte) (resLength & 0xff);
+        // The value field of this attribute begins with the 2-byte RES Length, which identifies
+        // the exact length of the RES in bits.
+        byte[] resBitLength = BytesConverter.convertIntegerTo4Bytes(res.length * 8);
+        message[index++] = resBitLength[2];
+        message[index++] = resBitLength[3];
+        System.arraycopy(res, 0, message, index, res.length);
+        index += res.length;
+
+        // set up AT_MAC, RFC 4187, 10.15 AT_MAC
+        message[index++] = ATTRIBUTE_MAC;
+        // fixed length, 5*4 = 20
+        message[index++] = 0x05;
+        // With two bytes reserved
+        message[index++] = 0x00;
+        message[index++] = 0x00;
+
+        // The MAC is calculated over the whole EAP packet and concatenated with optional
+        // message-specific data, with the exception that the value field of the
+        // MAC attribute is set to zero when calculating the MAC.
+        for (int i = 0; i < 16; i++) {
+            message[index++] = 0x00;
+        }
+
+        return message;
+    }
+
+    // See RFC 4187, 10.15 AT_MAC, snippet as below, the key must be k_aut
+    //
+    // The MAC algorithm is HMAC-SHA1-128 [RFC2104] keyed hash value.  (The
+    // HMAC-SHA1-128 value is obtained from the 20-byte HMAC-SHA1 value by
+    // truncating the output to 16 bytes.  Hence, the length of the MAC is
+    // 16 bytes.)  The derivation of the authentication key (K_aut) used in
+    // the calculation of the MAC is specified in Section 7.
+    @Nullable
+    private byte[] calculateMac(byte[] key, byte[] message) {
+        try {
+            Mac mac = Mac.getInstance(ALGORITHM_HMAC_SHA1);
+            SecretKeySpec secret = new SecretKeySpec(key, ALGORITHM_HMAC_SHA1);
+            mac.init(secret);
+            byte[] output = mac.doFinal(message);
+
+            if (output == null || output.length != SHA1_OUTPUT_LENGTH) {
+                Log.e(TAG, "Invalid result! length should be 20, but " + output.length);
+                return null;
+            }
+
+            byte[] macValue = new byte[16];
+            System.arraycopy(output, 0, macValue, 0, 16);
+            return macValue;
+        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+            Log.e(TAG, "calculateMac failed!", e);
+        }
+
+        return null;
+    }
+}
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java b/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java
new file mode 100644
index 0000000..bb451fd
--- /dev/null
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+
+/**
+ * Provide format to handle request/response SIM Authentication with GSM/3G security context.
+ *
+ * <p>Reference ETSI TS 131 102, Section 7.1.2.1 GSM/3G security context.
+ */
+class EapAkaSecurityContext {
+    private static final String TAG = "ServiceEntitlement";
+
+    private static final byte RESPONSE_TAG_SUCCESS = (byte) 0xDB;
+
+    private boolean valid;
+
+    /* Authentication result from SIM */
+    private byte[] res;
+    /* Cipher Key */
+    private byte[] ck;
+    /* Integrity Key */
+    private byte[] ik;
+
+    private EapAkaSecurityContext() {
+    }
+
+    /** Provide {@link EapAkaSecurityContext} from response data. */
+    public static EapAkaSecurityContext from(String response)
+            throws ServiceEntitlementException {
+        EapAkaSecurityContext securityContext = new EapAkaSecurityContext();
+        securityContext.parseResponseData(response);
+        if (!securityContext.isValid()) {
+            throw new ServiceEntitlementException("Invalid SIM EAP-AKA authentication response!");
+        }
+        return securityContext;
+    }
+
+    /**
+     * Parses SIM EAP-AKA Authentication responsed data and returns valid {@link
+     * EapAkaSecurityContext}
+     * for successful data; otherwise, returns invalid.
+     */
+    void parseResponseData(String response) {
+        if (TextUtils.isEmpty(response)) {
+            Log.d(TAG, "parseResponseData but input empty data!");
+            return;
+        }
+
+        try {
+            byte[] data = Base64.decode(response, Base64.DEFAULT);
+            Log.d(TAG, "decoded data length=" + data.length);
+
+            if (data.length <= 2) {
+                return;
+            }
+
+            int index = 0;
+
+            // check tag
+            if (data[index] != RESPONSE_TAG_SUCCESS) {
+                Log.d(TAG, "Not successful data, tag=" + data[index]);
+                return;
+            }
+
+            // Parse RES
+            index++; // move to RES length byte
+            res = parseTag(index, data);
+            if (res == null) {
+                Log.d(TAG, "Invalid data! can't parse RES!");
+                return;
+            }
+            // Parse CK
+            index += res.length + 1; // move to CK length byte
+            ck = parseTag(index, data);
+            if (ck == null) {
+                Log.d(TAG, "Invalid data! can't parse CK!");
+                return;
+            }
+            // Parse IK
+            index += ck.length + 1; // move to IK length byte
+            ik = parseTag(index, data);
+            if (ik == null) {
+                Log.d(TAG, "Invalid data! can't parse IK!");
+                return;
+            }
+
+            valid = true;
+        } catch (IllegalArgumentException illegalArgumentException) {
+            Log.e(TAG, "Invalid base-64 content");
+        }
+    }
+
+
+    private byte[] parseTag(int index, byte[] src) {
+        // index at the length byte
+        if (index >= src.length) {
+            Log.d(TAG, "No length byte!");
+            return null;
+        }
+        int length = src[index] & 0xff;
+        if (index + length >= src.length) {
+            Log.d(TAG, "Invalid data length!");
+            return null;
+        }
+        index++; // move to first byte of tag value
+        byte[] dest = new byte[length];
+        System.arraycopy(src, index, dest, 0, length);
+
+        return dest;
+    }
+
+    /** Returns {@code valid}. */
+    boolean isValid() {
+        return valid;
+    }
+
+    /** Returns {@code res}. */
+    public byte[] getRes() {
+        return res;
+    }
+
+    /** Returns {@code ck}. */
+    public byte[] getCk() {
+        return ck;
+    }
+
+    /** Returns {@code ik}. */
+    public byte[] getIk() {
+        return ik;
+    }
+}
diff --git a/java/com/android/libraries/entitlement/eapaka/MasterKey.java b/java/com/android/libraries/entitlement/eapaka/MasterKey.java
new file mode 100644
index 0000000..652fa65
--- /dev/null
+++ b/java/com/android/libraries/entitlement/eapaka/MasterKey.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.eapaka.utils.BytesConverter;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import androidx.annotation.Nullable;
+
+/**
+ * The class for Master Key.
+ *
+ * <p>Reference : RFC 4187, Section 7. Key Generation MK = SHA1(Identity|IK|CK)
+ */
+class MasterKey {
+    private static final String TAG = "ServiceEntitlement";
+    /* K_encr (128 bits) */
+    private static final int LENGTH_K_ENCR = 16;
+    /* K_aut (128 bits) */
+    private static final int LENGTH_K_AUT = 16;
+    /* Master Session Key (64 bytes) */
+    private static final int LENGTH_MSK = 64;
+    /* Extended Master Session Key (64 bytes) */
+    private static final int LENGTH_EMSK = 64;
+    /* Transient EAP Keys : K_enrc + K_aut + MSK + EMSK */
+    private static final int LENGTH_TEKS = 160;
+
+    /* Master Key */
+    private byte[] masterKey;
+
+    /* Transient EAP Keys */
+    private byte[] encr;
+    private byte[] aut;
+    private byte[] msk;
+    private byte[] emsk;
+
+    private MasterKey() {
+    }
+
+    /** Create the {@code masterKey}. */
+    public static MasterKey create(String identity, @Nullable byte[] ik, @Nullable byte[] ck)
+            throws ServiceEntitlementException {
+        if (TextUtils.isEmpty(identity)
+                || ik == null
+                || ik.length == 0
+                || ck == null
+                || ck.length == 0) {
+            Log.d(TAG, "Can't create master key due to invalid input!");
+            return null;
+        }
+        MasterKey mk = new MasterKey();
+        mk.from(identity, ik, ck);
+        return mk;
+    }
+
+    void from(String identity, byte[] ik, byte[] ck) {
+        // concatenate Identity/IK/CK
+        byte[] identityBytes = identity.getBytes(UTF_8);
+        byte[] data = new byte[identityBytes.length + ik.length + ck.length];
+        int index = 0;
+        System.arraycopy(identityBytes, 0, data, index, identityBytes.length);
+        index += identityBytes.length;
+        System.arraycopy(ik, 0, data, index, ik.length);
+        index += ik.length;
+        System.arraycopy(ck, 0, data, index, ck.length);
+
+        // process SHA1
+        try {
+            MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+            messageDigest.update(data);
+            masterKey = messageDigest.digest();
+        } catch (NoSuchAlgorithmException e) {
+            Log.d(TAG, "process sHA-1 failed", e);
+        }
+
+        // Generate TEKs
+        generateTransientEapKeys();
+    }
+
+    /**
+     * Generates TEKs base on RFC 4187, Section 7. Key Generation, snippet as below
+     *
+     * <p>The Master Key is fed into a Pseudo-Random number Function (PRF), which generates
+     * separate
+     * Transient EAP Keys (TEKs) for protecting EAP-AKA packets, as well as a Master Session Key
+     * (MSK)
+     * for link layer security and an Extended Master Session Key (EMSK) for other purposes.
+     */
+    void generateTransientEapKeys() {
+        byte[] teks = generatePsudoRandomNumber();
+
+        if (teks == null || teks.length != 160) {
+            Log.e(TAG, "Invalid TEKs data!");
+            return;
+        }
+
+        int index = 0;
+        encr = new byte[LENGTH_K_ENCR];
+        System.arraycopy(teks, index, encr, 0, LENGTH_K_ENCR);
+        index += LENGTH_K_ENCR;
+        aut = new byte[LENGTH_K_AUT];
+        System.arraycopy(teks, index, aut, 0, LENGTH_K_AUT);
+        index += LENGTH_K_AUT;
+        msk = new byte[LENGTH_MSK];
+        System.arraycopy(teks, index, msk, 0, LENGTH_MSK);
+        index += LENGTH_MSK;
+        emsk = new byte[LENGTH_EMSK];
+        System.arraycopy(teks, index, emsk, 0, LENGTH_EMSK);
+    }
+
+    /** Returns {@code aut}. */
+    public byte[] getAut() {
+        return aut;
+    }
+
+    // RFC 4187 Appendix A.  Pseudo-Random Number Generator
+    @Nullable
+    private byte[] generatePsudoRandomNumber() {
+        // Step 1: Choose a new, secret value for the seed-key, XKEY
+        byte[] key = masterKey;
+
+        // 160-bit XKEY and XVAL values are used, so b = 160.  On each full
+        // authentication, the Master Key is used as the initial secret seed-key
+        // XKEY.
+        if (key == null || key.length != 20) {
+            Log.e(TAG, "Not a valid XKey!length=" + (key == null ? "null" : key.length));
+            return null;
+        }
+
+        // Step 2: In hexadecimal notation let
+        //     t = 67452301 EFCDAB89 98BADCFE 10325476 C3D2E1F0
+        //     This is the initial value for H0|H1|H2|H3|H4
+        //     in the FIPS SHS [SHA-1]
+        int[] t = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
+
+        // Step 3: For j = 0 to m - 1 do
+        //       3.1.  XSEED_j = 0 /* no optional user input */
+        //       3.2.  For i = 0 to 1 do
+        //             a.  XVAL = (XKEY + XSEED_j) mod 2^b
+        //             b.  w_i = G(t, XVAL)
+        //             c.  XKEY = (1 + XKEY + w_i) mod 2^b
+        //       3.3.  x_j = w_0|w_1
+        // Step 3: For j = 0 to m - 1 do
+        //
+        // Base on below snippet from RFC 4187, b is 160, x_j is 40 bytes, w_i is 20 bytes, TEKs
+        // length is 160 bytes and m is 160/40=4
+        //
+        // 160-bit XKEY and XVAL values are used, so b = 160.  On each full
+        // authentication, the Master Key is used as the initial secret seed-key
+        // XKEY.  The optional user input values (XSEED_j) in step 3.1 are set
+        // to zero.
+        // On full authentication, the resulting 320-bit random numbers x_0,
+        // x_1, ..., x_m-1 are concatenated and partitioned into suitable-sized
+        // chunks and used as keys in the following order: K_encr (128 bits),
+        // K_aut (128 bits), Master Session Key (64 bytes), Extended Master
+        // Session Key (64 bytes).
+        byte[] teks = new byte[LENGTH_TEKS];
+        int index = 0;
+        for (int j = 0; j < 4; j++) {
+            // 3.1.  XSEED_j = 0, do nothing
+            // 3.2.  For i = 0 to 1 do
+            for (int i = 0; i < 2; i++) {
+                // a.  XVAL = (XKEY + XSEED_j) mod 2^b
+                byte[] val = key;
+
+                // b.  w_i = G(t, XVAL)
+                byte[] w = doFunctionG(t, val);
+                if (w == null || w.length != 20) {
+                    Log.e(TAG, "Get invalid w value from G function!");
+                    return null;
+                }
+                // fill w to teks
+                System.arraycopy(w, 0, teks, index, 20);
+                index += 20;
+
+                // c.  XKEY = (1 + XKEY + w_i) mod 2^b
+                // XKEY is 20 bytes, 160 bits, mod 2^160 is for make sure XKEY just 160 bits
+                int carry = 1;
+                for (int k = 19; k >= 0; k--) {
+                    carry += (key[k] & 0xff) + (w[k] & 0xff);
+                    key[k] = (byte) (carry & 0xff);
+                    // shift one byte and keep carry for next byte calculate
+                    carry >>= 8;
+                }
+            }
+            // 3.3.  x_j = w_0|w_1, already copy w_0/w_1 to output
+        }
+
+        return teks;
+    }
+
+    // See FIPS 186-2 APPENDIX 3.3. CONSTRUCTING THE FUNCTION G FROM THE SHA-1, snippet as below
+    //
+    // G(t,c) may be constructed using steps (a) - (e) in section 7 of the Specifications for the
+    // Secure Hash Standard. Before executing these steps, {Hj} and M1 must be initialized as
+    // follows:
+    //
+    // i. Initialize the {Hj} by dividing the 160 bit value t into five 32-bit segments as follows:
+    // t = t0 || t1 || t2 || t3 || t4
+    //  Then Hj = tj for j = 0 through 4.
+    //
+    // ii. There will be only one message block, M1, which is initialized as follows:
+    //  M1 = c || 0^(512-b)
+    //  (The first b bits of M1 contain c, and the remaining (512-b) bits are set to zero).
+    //
+    // Then steps (a) through (e) of section 7 are executed, and G(t,c) is the 160 bit string
+    // represented by the five words:
+    //  H0 || H1 || H2 || H3 || H4
+    // at the end of step (e).
+    private byte[] doFunctionG(int[] t, byte[] c) {
+        // i. Initialize the {Hj} by dividing the 160 bit value t into five 32-bit segments
+        // 5 segments and every segments is 32 bits/4 bytes
+        byte[][] bytesH = new byte[5][4];
+        for (int i = 0; i < 5; i++) {
+            System.arraycopy(BytesConverter.convertIntegerTo4Bytes(t[i]), 0, bytesH[i], 0, 4);
+        }
+
+        // ii. init message block, M1
+        // The first b bits of M1 contain c, and the remaining (512-b) bits are set to zero
+        byte[] bytesM1 = new byte[64];
+        System.arraycopy(c, 0, bytesM1, 0, 20);
+        for (int i = 20; i < 64; i++) {
+            bytesM1[i] = 0x00;
+        }
+
+        // See FIPS PUB 180-1, Secure Hash Standard
+        //     Section 7. COMPUTING THE MESSAGE DIGEST which defined steps (a) - (e)
+
+        // The words of the 80-word sequence are labeled W0, W1,..., W79.
+        byte[][] bytesW = new byte[80][4];
+
+        // a. Divide Mi into 16 words W0, W1, ... , W15, where W0 is the left-most word.
+        for (int i = 0; i < 16; i++) {
+            System.arraycopy(bytesM1, i * 4, bytesW[i], 0, 4);
+        }
+
+        // b. For t = 16 to 79 let Wt = S^1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16).
+        for (int i = 16; i < 80; i++) {
+            bytesW[i] =
+                    doFunctionS(1,
+                            doXor(bytesW[i - 3], bytesW[i - 8], bytesW[i - 14], bytesW[i - 16]));
+        }
+
+        // c. Let A = H0, B = H1, C = H2, D = H3, E = H4.
+        byte[] bytesA = new byte[4];
+        byte[] bytesB = new byte[4];
+        byte[] bytesC = new byte[4];
+        byte[] bytesD = new byte[4];
+        byte[] bytesE = new byte[4];
+        System.arraycopy(bytesH[0], 0, bytesA, 0, 4);
+        System.arraycopy(bytesH[1], 0, bytesB, 0, 4);
+        System.arraycopy(bytesH[2], 0, bytesC, 0, 4);
+        System.arraycopy(bytesH[3], 0, bytesD, 0, 4);
+        System.arraycopy(bytesH[4], 0, bytesE, 0, 4);
+
+        // d. For t = 0 to 79 do
+        //    TEMP = S^5(A) + ft(B,C,D) + E + Wt + Kt;
+        //    E = D; D = C; C = S^30(B); B = A; A = TEMP;
+        for (int i = 0; i < 80; i++) {
+            int tmpA = new BigInteger(doFunctionS(5, bytesA)).intValue();
+            int tmpF = doFunctionF(i, bytesB, bytesC, bytesD);
+            int tmpE = new BigInteger(bytesE).intValue();
+            int tmpW = new BigInteger(bytesW[i]).intValue();
+            int tmpK = doFunctionK(i);
+            int temp = tmpA + tmpF + tmpE + tmpW + tmpK;
+            bytesE = bytesD;
+            bytesD = bytesC;
+            bytesC = doFunctionS(30, bytesB);
+            bytesB = bytesA;
+            bytesA = BytesConverter.convertIntegerTo4Bytes(temp);
+        }
+
+        // e. Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.
+        bytesH[0] = addTwoBytes(bytesH[0], bytesA);
+        bytesH[1] = addTwoBytes(bytesH[1], bytesB);
+        bytesH[2] = addTwoBytes(bytesH[2], bytesC);
+        bytesH[3] = addTwoBytes(bytesH[3], bytesD);
+        bytesH[4] = addTwoBytes(bytesH[4], bytesE);
+
+        // After processing Mn, the message digest is the 160-bit string represented by the 5 words
+        // H0 H1 H2 H3 H4.
+        byte[] output = new byte[20];
+        System.arraycopy(bytesH[0], 0, output, 0, 4);
+        System.arraycopy(bytesH[1], 0, output, 4, 4);
+        System.arraycopy(bytesH[2], 0, output, 8, 4);
+        System.arraycopy(bytesH[3], 0, output, 12, 4);
+        System.arraycopy(bytesH[4], 0, output, 16, 4);
+
+        return output;
+    }
+
+    private static byte[] addTwoBytes(byte[] a, byte[] b) {
+        BigInteger iA = new BigInteger(a);
+        BigInteger iB = new BigInteger(b);
+        return BytesConverter.convertIntegerTo4Bytes(iA.add(iB).intValue());
+    }
+
+    // See FIPS PUB 180-1, Section 3. OPERATIONS ON WORDS
+    // Sn(X) = (X << n) OR (X >> 32-n).
+    private static byte[] doFunctionS(int n, byte[] dataX) {
+        BigInteger leftShiftValue = new BigInteger(dataX).shiftLeft(n);
+
+        // BigInteger.shiftRight would fill 1 if the left-most bit is 1, so use '>>>'
+        int value = new BigInteger(dataX).intValue();
+        value = value >>> (32 - n); // X should be 32 bits
+        BigInteger rightShiftValue = BigInteger.valueOf(value);
+        BigInteger result = leftShiftValue.or(rightShiftValue);
+        return BytesConverter.convertIntegerTo4Bytes(result.intValue());
+    }
+
+    private static byte[] doXor(byte[] a, byte[] b, byte[] c, byte[] d) {
+        BigInteger iA = new BigInteger(a);
+        BigInteger iB = new BigInteger(b);
+        BigInteger iC = new BigInteger(c);
+        BigInteger iD = new BigInteger(d);
+        BigInteger result = iA.xor(iB).xor(iC).xor(iD);
+        return BytesConverter.convertIntegerTo4Bytes(result.intValue());
+    }
+
+    // See FIPS PUB 180-1, Section 5. FUNCTIONS USED
+    // A sequence of logical functions f0, f1,..., f79 is used in the SHA-1. Each ft, 0 <= t <= 79,
+    // operates on three 32-bit words B, C, D and produces a 32-bit word as output. ft(B,C,D) is
+    // defined as follows: for words B, C, D,
+    //
+    // ft(B,C,D) = (B AND C) OR ((NOT B) AND D) (0 <= t <= 19)
+    // ft(B,C,D) = B XOR C XOR D (20 <= t <= 39)
+    // ft(B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
+    // ft(B,C,D) = B XOR C XOR D (60 <= t <= 79).
+    private static int doFunctionF(int t, byte[] b, byte[] c, byte[] d) {
+        BigInteger iB = new BigInteger(b);
+        BigInteger iC = new BigInteger(c);
+        BigInteger iD = new BigInteger(d);
+        BigInteger result = BigInteger.valueOf(-1);
+        if (0 <= t && t <= 19) {
+            result = iB.and(iC).or(iB.not().and(iD));
+        } else if (20 <= t && t <= 39) {
+            result = iB.xor(iC).xor(iD);
+        } else if (40 <= t && t <= 59) {
+            result = iB.and(iC).or(iB.and(iD)).or(iC.and(iD));
+        } else if (60 <= t && t <= 79) {
+            result = iB.xor(iC).xor(iD);
+        }
+
+        return result.intValue();
+    }
+
+    // See FIPS PUB 180-1, Section 6. CONSTANTS USED
+    //
+    // A sequence of constant words K(0), K(1), ... , K(79) is used in the SHA-1. In hex these are
+    // given by
+    // K = 5A827999 ( 0 <= t <= 19)
+    // Kt = 6ED9EBA1 (20 <= t <= 39)
+    // Kt = 8F1BBCDC (40 <= t <= 59)
+    // Kt = CA62C1D6 (60 <= t <= 79).
+    private static int doFunctionK(int t) {
+        if (0 <= t && t <= 19) {
+            return 0x5A827999;
+        } else if (20 <= t && t <= 39) {
+            return 0x6ED9EBA1;
+        } else if (40 <= t && t <= 59) {
+            return 0x8F1BBCDC;
+        } else if (60 <= t && t <= 79) {
+            return 0xCA62C1D6;
+        }
+
+        return -1;
+    }
+}
diff --git a/java/com/android/libraries/entitlement/http/HttpClient.java b/java/com/android/libraries/entitlement/http/HttpClient.java
new file mode 100644
index 0000000..79f42ff
--- /dev/null
+++ b/java/com/android/libraries/entitlement/http/HttpClient.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.http;
+
+import static com.android.libraries.entitlement.http.HttpConstants.RequestMethod.POST;
+
+import static com.google.common.base.Strings.nullToEmpty;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
+import com.android.libraries.entitlement.utils.StreamUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Map;
+
+import androidx.annotation.WorkerThread;
+
+/** Implement the HTTP request method according to TS.43 specification. */
+public class HttpClient {
+    private static final String TAG = "ServiceEntitlement";
+    private static final boolean DEBUG = false; // STOPSHIP if true
+
+    private static final int SOCKET_TIMEOUT_VALUE = (int) SECONDS.toMillis(30);
+    private static final int CONNECT_TIMEOUT_VALUE = (int) SECONDS.toMillis(30);
+
+    private HttpURLConnection connection;
+
+    @WorkerThread
+    // TODO(b/177544547): Add debug messages
+    public HttpResponse request(HttpRequest request) throws ServiceEntitlementException {
+        try {
+            logd("HttpClient.request url: " + request.url());
+            createConnection(request);
+            if (connection == null) {
+                logd("HttpClient.request connection is null");
+                throw new ServiceEntitlementException("No connection");
+            }
+            logd("HttpClient.request headers (partial): " + connection.getRequestProperties());
+            if (POST.equals(request.requestMethod())) {
+                try (OutputStream out = new DataOutputStream(connection.getOutputStream())) {
+                    out.write(request.postData().toString().getBytes(UTF_8));
+                    logd("HttpClient.request post data: " + request.postData());
+                }
+            }
+            connection.connect(); // This is to trigger SocketTimeoutException early
+            HttpResponse response = getHttpResponse(connection);
+            Log.d(TAG, "HttpClient.response : " + response);
+            return response;
+        } catch (IOException e) {
+            InputStream errorStream = connection.getErrorStream();
+            Log.e(
+                    TAG,
+                    "HttpClient.request() error: " + StreamUtils.inputStreamToStringSafe(
+                            errorStream));
+            throw new ServiceEntitlementException("request failed! exception: " + e.getMessage());
+        } finally {
+            closeConnection();
+        }
+    }
+
+    private void createConnection(HttpRequest request) throws ServiceEntitlementException {
+        try {
+            URL url = new URL(request.url());
+            connection = (HttpURLConnection) url.openConnection();
+
+            // add HTTP headers
+            for (Map.Entry<String, String> entry : request.requestProperties().entrySet()) {
+                connection.addRequestProperty(entry.getKey(), entry.getValue());
+            }
+
+            // set parameters
+            connection.setRequestMethod(request.requestMethod());
+            connection.setConnectTimeout(CONNECT_TIMEOUT_VALUE);
+            connection.setReadTimeout(SOCKET_TIMEOUT_VALUE);
+            if (POST.equals(request.requestMethod())) {
+                connection.setDoOutput(true);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "IOException: " + e.getMessage());
+            throw new ServiceEntitlementException("Configure connection failed!" + e.getMessage());
+        }
+    }
+
+    private void closeConnection() {
+        if (connection != null) {
+            connection.disconnect();
+            connection = null;
+        }
+    }
+
+    private static HttpResponse getHttpResponse(HttpURLConnection connection)
+            throws ServiceEntitlementException {
+        try {
+            int responseCode = connection.getResponseCode();
+            logd("HttpClient.response headers: " + connection.getHeaderFields());
+            if (responseCode != HttpURLConnection.HTTP_OK) {
+                throw new ServiceEntitlementException(
+                  ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, responseCode, null,
+                  "Invalid connection response", null);
+            }
+            String responseBody = readResponse(connection);
+            logd("HttpClient.response body: " + responseBody);
+            return HttpResponse.builder()
+                    .setContentType(getContentType(connection))
+                    .setBody(responseBody)
+                    .setResponseCode(responseCode)
+                    .setResponseMessage(nullToEmpty(connection.getResponseMessage()))
+                    .build();
+        } catch (IOException e) {
+            throw new ServiceEntitlementException(
+              ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, 0,  null,
+              "Read response failed!", e);
+        }
+    }
+
+    private static String readResponse(URLConnection connection) throws IOException {
+        try (InputStream in = connection.getInputStream()) {
+            return StreamUtils.inputStreamToStringSafe(in);
+        }
+    }
+
+    private static int getContentType(URLConnection connection) {
+        String contentType = connection.getHeaderField(ContentType.NAME);
+        if (TextUtils.isEmpty(contentType)) {
+            return ContentType.UNKNOWN;
+        }
+
+        if (contentType.contains("xml")) {
+            return ContentType.XML;
+        } else if ("text/vnd.wap.connectivity".equals(contentType)) {
+            // Workaround that a server vendor uses this type for XML
+            return ContentType.XML;
+        } else if (contentType.contains("json")) {
+            return ContentType.JSON;
+        }
+        return ContentType.UNKNOWN;
+    }
+
+    private static void logd(String message) {
+        if (DEBUG) {
+            Log.d(TAG, message);
+        }
+    }
+}
diff --git a/java/com/android/libraries/entitlement/http/HttpConstants.java b/java/com/android/libraries/entitlement/http/HttpConstants.java
new file mode 100644
index 0000000..c4ed5e2
--- /dev/null
+++ b/java/com/android/libraries/entitlement/http/HttpConstants.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.http;
+
+/** Http constants using for entitlement flow of TS.43. */
+public final class HttpConstants {
+    private HttpConstants() {}
+
+    /** Possible request methods for Entitlement server response. */
+    public static final class RequestMethod {
+        private RequestMethod() {}
+
+        public static final String GET = "GET";
+        public static final String POST = "POST";
+    }
+
+    /** Possible content type for Entitlement server response. */
+    public static final class ContentType {
+        private ContentType() {}
+
+        public static final int UNKNOWN = -1;
+        public static final int JSON = 0;
+        public static final int XML = 1;
+
+        public static final String NAME = "Content-Type";
+    }
+}
diff --git a/java/com/android/libraries/entitlement/http/HttpRequest.java b/java/com/android/libraries/entitlement/http/HttpRequest.java
new file mode 100644
index 0000000..af81b10
--- /dev/null
+++ b/java/com/android/libraries/entitlement/http/HttpRequest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.http;
+
+import android.util.ArrayMap;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+import org.json.JSONObject;
+
+/** The parameters of the http request. */
+@AutoValue
+public abstract class HttpRequest {
+
+    public abstract String url();
+
+    public abstract String requestMethod();
+
+    public abstract JSONObject postData();
+
+    public abstract ImmutableMap<String, String> requestValues();
+
+    public abstract ImmutableMap<String, String> requestProperties();
+
+    /** Builder of {@link HttpRequest}. */
+    @AutoValue.Builder
+    public abstract static class Builder {
+
+        private final Map<String, String> values = new ArrayMap<>();
+        private final Map<String, String> properties = new ArrayMap<>();
+
+        public abstract HttpRequest build();
+
+        public abstract Builder setUrl(String url);
+
+        public abstract Builder setRequestMethod(String requestMethod);
+
+        public abstract Builder setPostData(JSONObject postData);
+
+        abstract Builder setRequestValues(ImmutableMap<String, String> value);
+
+        abstract Builder setRequestProperties(ImmutableMap<String, String> properties);
+
+        public Builder addRequestValues(String key, String value) {
+            values.put(key, value);
+            return this.setRequestValues(ImmutableMap.copyOf(values));
+        }
+
+        public Builder addRequestProperty(String key, String value) {
+            properties.put(key, value);
+            return this.setRequestProperties(ImmutableMap.copyOf(properties));
+        }
+    }
+
+    public static Builder builder() {
+        return new AutoValue_HttpRequest.Builder()
+                .setUrl("")
+                .setRequestMethod("")
+                .setPostData(new JSONObject())
+                .setRequestValues(ImmutableMap.of())
+                .setRequestProperties(ImmutableMap.of());
+    }
+}
diff --git a/java/com/android/libraries/entitlement/http/HttpResponse.java b/java/com/android/libraries/entitlement/http/HttpResponse.java
new file mode 100644
index 0000000..1cb165e
--- /dev/null
+++ b/java/com/android/libraries/entitlement/http/HttpResponse.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.http;
+
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
+
+import com.google.auto.value.AutoValue;
+
+/** The response of the http request. */
+@AutoValue
+public abstract class HttpResponse {
+
+    /** Content type of the response. */
+    public abstract int contentType();
+
+    public abstract String body();
+
+    public abstract int responseCode();
+
+    public abstract String responseMessage();
+
+    /** Builder of {@link HttpResponse}. */
+    @AutoValue.Builder
+    public abstract static class Builder {
+
+        public abstract HttpResponse build();
+
+        public abstract Builder setContentType(int contentType);
+
+        public abstract Builder setBody(String body);
+
+        public abstract Builder setResponseCode(int responseCode);
+
+        public abstract Builder setResponseMessage(String responseMessage);
+    }
+
+    public static Builder builder() {
+        return new AutoValue_HttpResponse.Builder()
+                .setContentType(ContentType.UNKNOWN)
+                .setBody("")
+                .setResponseCode(0)
+                .setResponseMessage("");
+    }
+
+    @Override
+    public final String toString() {
+        return new StringBuilder("HttpResponse{")
+                .append("contentType=")
+                .append(contentType())
+                .append(" body=(")
+                .append(body().length())
+                .append(" characters)")
+                .append(" responseCode=")
+                .append(responseCode())
+                .append(" responseMessage=")
+                .append(responseMessage())
+                .append("}")
+                .toString();
+    }
+}
diff --git a/java/com/android/libraries/entitlement/utils/BytesConverter.java b/java/com/android/libraries/entitlement/utils/BytesConverter.java
new file mode 100644
index 0000000..034ac9c
--- /dev/null
+++ b/java/com/android/libraries/entitlement/utils/BytesConverter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka.utils;
+
+import java.nio.ByteBuffer;
+
+import androidx.annotation.Nullable;
+
+public class BytesConverter {
+    private static final int INTEGER_SIZE = 4; // 4 bytes
+
+    // A table mapping from a number to a hex character for fast encoding hex strings.
+    private static final char[] HEX_CHARS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
+    /**
+     * Converts a byte array into a String of hexadecimal characters.
+     *
+     * @param bytes an array of bytes
+     * @return hex string representation of bytes array
+     */
+    @Nullable
+    public static String convertBytesToHexString(byte[] bytes) {
+        if (bytes == null) {
+            return null;
+        }
+
+        StringBuilder ret = new StringBuilder(2 * bytes.length);
+
+        for (int i = 0; i < bytes.length; i++) {
+            int b;
+            b = 0x0f & (bytes[i] >> 4);
+            ret.append(HEX_CHARS[b]);
+            b = 0x0f & bytes[i];
+            ret.append(HEX_CHARS[b]);
+        }
+
+        return ret.toString();
+    }
+
+    /** Converts integer to 4 bytes. */
+    public static byte[] convertIntegerTo4Bytes(int value) {
+        return ByteBuffer.allocate(INTEGER_SIZE).putInt(value).array();
+    }
+}
diff --git a/java/com/android/libraries/entitlement/utils/StreamUtils.java b/java/com/android/libraries/entitlement/utils/StreamUtils.java
new file mode 100644
index 0000000..1ff2ffc
--- /dev/null
+++ b/java/com/android/libraries/entitlement/utils/StreamUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.utils;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/** Utility methods about InputStream. */
+public final class StreamUtils {
+
+    private static final int BUFFER_SIZE = 1024;
+
+    private StreamUtils() {
+    }
+
+    /** Reads an {@link InputStream} into a string. */
+    public static String inputStreamToString(InputStream inputStream) throws IOException {
+        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
+             ByteArrayOutputStream result = new ByteArrayOutputStream()) {
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int length = 0;
+            while ((length = inputStream.read(buffer)) != -1) {
+                result.write(buffer, 0, length);
+            }
+            return result.toString(StandardCharsets.UTF_8.name());
+        }
+    }
+
+    /** Reads an {@link InputStream} into a string. Returns an empty string if any error. */
+    public static String inputStreamToStringSafe(InputStream inputStream) {
+        try {
+            return inputStreamToString(inputStream);
+        } catch (IOException e) {
+            return "";
+        }
+    }
+}
diff --git a/java/com/google/android/libraries/entitlement/CarrierData.java b/java/com/google/android/libraries/entitlement/CarrierData.java
deleted file mode 100644
index 2153fda..0000000
--- a/java/com/google/android/libraries/entitlement/CarrierData.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 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.google.android.libraries.entitlement;
-
-import com.google.auto.value.AutoValue;
-
-/**
- * Carrier specific customization to be used in the service entitlement queries and operations.
- *
- * @see #ServiceEntitlement
- */
-@AutoValue
-public abstract class CarrierData {
-  /**
-   * The carrier's entitlement server URL. If not set, will use {@code
-   * https://aes.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org} as defined in GSMA spec TS.43 section 2.1.
-   */
-  public abstract String serverUrl();
-
-  // Builder...
-}
diff --git a/java/com/google/android/libraries/entitlement/EsimOdsaOperation.java b/java/com/google/android/libraries/entitlement/EsimOdsaOperation.java
deleted file mode 100644
index 1247824..0000000
--- a/java/com/google/android/libraries/entitlement/EsimOdsaOperation.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2020 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.google.android.libraries.entitlement;
-
-import com.google.auto.value.AutoValue;
-
-/**
- * HTTP request parameters specific to on device service actiavation (ODSA). See GSMA spec TS.43
- * section 6.2.
- */
-@AutoValue
-public abstract class EsimOdsaOperation {
-  /** OSDA operation: CheckEligibility. */
-  public static final String OPERATION_CHECK_ELIGIBILITY = "CheckEligibility";
-  /** OSDA operation: ManageSubscription. */
-  public static final String OPERATION_MANAGE_SUBSCRIPTION = "ManageSubscription";
-  /** OSDA operation: ManageService. */
-  public static final String OPERATION_MANAGE_SERVICE = "ManageService";
-  /** OSDA operation: AcquireConfiguration. */
-  public static final String OPERATION_ACQUIRE_CONFIGURATION = "AcquireConfiguration";
-
-  /** Indicates that operation_type is not set. */
-  static final int OPERATION_TYPE_NOT_SET = -1;
-  /** To activate a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
-  public static final int OPERATION_TYPE_SUBSCRIBE = 0;
-  /** To cancel a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
-  public static final int OPERATION_TYPE_UNSUBSCRIBE = 1;
-  /** To manage an existing subscription, for {@link #OPERATION_MANAGE_SUBSCRIPTION}. */
-  public static final int OPERATION_TYPE_CHANGE_SUBSCRIPTION = 2;
-  /**
-   * To transfer a subscription from an existing device, used by {@link
-   * #OPERATION_MANAGE_SUBSCRIPTION}.
-   */
-  public static final int OPERATION_TYPE_TRANSFER_SUBSCRIPTION = 3;
-  /**
-   * To inform the network of a subscription update, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}.
-   */
-  public static final int OPERATION_TYPE_UPDATE_SUBSCRIPTION = 4;
-  /** To activate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */
-  public static final int OPERATION_TYPE_ACTIVATE_SERVICE = 10;
-  /** To deactivate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */
-  public static final int OPERATION_TYPE_DEACTIVATE_SERVICE = 11;
-
-  /** Indicates the companion device carries the same MSISDN as the primary device. */
-  public static final String COMPANION_SERVICE_SHAERED_NUMBER = "SharedNumber";
-  /** Indicates the companion device carries a different MSISDN as the primary device. */
-  public static final String COMPANION_SERVICE_DIFFERENT_NUMBER = "DiffNumber";
-
-  /** Returns the eSIM ODSA operation. Used by HTTP parameter "operation". */
-  public abstract String operation();
-
-  /**
-   * Returns the detiled type of the eSIM ODSA operation. Used by HTTP parameter "operation_type".
-   */
-  public abstract int operationType();
-
-  /**
-   * Returns the unique identifier of the companion device, like IMEI. Used by HTTP parameter
-   * "companion_terminal_id".
-   */
-  public abstract String companionTerminalId();
-
-  /**
-   * Returns the OEM of the companion device. Used by HTTP parameter "companion_terminal_vendor".
-   */
-  public abstract String companionTerminalVendor();
-
-  /**
-   * Returns the model of the companion device. Used by HTTP parameter "companion_terminal_model".
-   */
-  public abstract String companionTerminalModel();
-
-  /**
-   * Returns the software version of the companion device. Used by HTTP parameter
-   * "companion_terminal_sw_version".
-   */
-  public abstract String companionTerminalSoftwareVersion();
-
-  /**
-   * Returns the user-friendly version of the companion device. Used by HTTP parameter
-   * "companion_terminal_friendly_name".
-   */
-  public abstract String companionTerminalFriendlyName();
-
-  /**
-   * Returns the service type of the companion device, e.g. if the MSISDN is same as the primary
-   * device. Used by HTTP parameter "companion_terminal_service".
-   */
-  public abstract String companionTerminalService();
-
-  /**
-   * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid".
-   */
-  public abstract String companionTerminalIccid();
-
-  /**
-   * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid".
-   */
-  public abstract String companionTerminalEid();
-
-  /** Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid". */
-  public abstract String terminalIccid();
-
-  /**
-   * Returns the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
-   * "terminal_eid".
-   */
-  public abstract String terminalEid();
-
-  /**
-   * Returns the unique identifier of the primary device eSIM, like the IMEI associated with the
-   * eSIM. Used by HTTP parameter "target_terminal_id".
-   */
-  public abstract String targetTerminalId();
-
-  /** Returns the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid". */
-  public abstract String targetTerminalIccid();
-
-  /**
-   * Returns the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
-   * "target_terminal_eid".
-   */
-  public abstract String targetTerminalEid();
-
-  /** Returns a new {@link Builder} object. */
-  public static Builder builder() {
-    return new AutoValue_EsimOdsaOperation.Builder().setOperationType(OPERATION_TYPE_NOT_SET);
-  }
-
-  /**
-   * Builder.
-   *
-   * <p>For ODSA, the rule of which parameters are required varies or each operation/opeation_type.
-   * The Javadoc below gives high-level description, but please refer to GMSA spec TS.43 section 6.2
-   * for details.
-   */
-  @AutoValue.Builder
-  public abstract static class Builder {
-    /**
-     * Sets the eSIM ODSA operation. Used by HTTP parameter "operation".
-     *
-     * <p>Required.
-     *
-     * @see #OPERATION_CHECK_ELIGIBILITY
-     * @see #OPERATION_MANAGE_SUBSCRIPTION
-     * @see #OPERATION_MANAGE_SERVICE
-     * @see #OPERATION_ACQUIRE_CONFIGURATION
-     */
-    public abstract Builder setOperation(String value);
-
-    /**
-     * Sets the detiled type of the eSIM ODSA operation. Used by HTTP parameter "operation_type" if
-     * set.
-     *
-     * <p>Required by some operation.
-     *
-     * @see #OPERATION_TYPE_SUBSCRIBE
-     * @see #OPERATION_TYPE_UNSUBSCRIBE
-     * @see #OPERATION_TYPE_CHANGE_SUBSCRIPTION
-     * @see #OPERATION_TYPE_TRANSFER_SUBSCRIPTION
-     * @see #OPERATION_TYPE_UPDATE_SUBSCRIPTION
-     * @see #OPERATION_TYPE_ACTIVATE_SERVICE
-     * @see #OPERATION_TYPE_DEACTIVATE_SERVICE
-     */
-    public abstract Builder setOperationType(int value);
-
-    /**
-     * Sets the unique identifier of the companion device, like IMEI. Used by HTTP parameter
-     * "companion_terminal_id" if set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalId(String value);
-
-    /**
-     * Sets the OEM of the companion device. Used by HTTP parameter "companion_terminal_vendor" if
-     * set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalVendor(String value);
-
-    /**
-     * Sets the model of the companion device. Used by HTTP parameter "companion_terminal_model" if
-     * set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalModel(String value);
-
-    /**
-     * Sets the software version of the companion device. Used by HTTP parameter
-     * "companion_terminal_sw_version" if set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalSoftwareVersion(String value);
-
-    /**
-     * Sets the user-friendly version of the companion device. Used by HTTP parameter
-     * "companion_terminal_friendly_name" if set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalFriendlyName(String value);
-
-    /**
-     * Sets the service type of the companion device, e.g. if the MSISDN is same as the primary
-     * device. Used by HTTP parameter "companion_terminal_service" if set.
-     *
-     * <p>Used by companion device ODSA operation.
-     *
-     * @see #COMPANION_SERVICE_SHAERED_NUMBER
-     * @see #COMPANION_SERVICE_DIFFERENT_NUMBER
-     */
-    public abstract Builder setCompanionTerminalService(String value);
-
-    /**
-     * Sets the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid" if
-     * set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalIccid(String value);
-
-    /**
-     * Sets the eUICC identifier (EID) of the companion device. Used by HTTP parameter
-     * "companion_terminal_eid" if set.
-     *
-     * <p>Used by companion device ODSA operation.
-     */
-    public abstract Builder setCompanionTerminalEid(String value);
-
-    /**
-     * Sets the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid" if set.
-     *
-     * <p>Used by primary device ODSA operation.
-     */
-    public abstract Builder setTerminalIccid(String value);
-
-    /**
-     * Sets the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
-     * "terminal_eid" if set.
-     *
-     * <p>Used by primary device ODSA operation.
-     */
-    public abstract Builder setTerminalEid(String value);
-
-    /**
-     * Sets the unique identifier of the primary device eSIM, like the IMEI associated with the
-     * eSIM. Used by HTTP parameter "target_terminal_id" if set.
-     *
-     * <p>Used by primary device ODSA operation.
-     */
-    public abstract Builder setTargetTerminalId(String value);
-
-    /**
-     * Sets the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid" if set.
-     *
-     * <p>Used by primary device ODSA operation.
-     */
-    public abstract Builder setTargetTerminalIccid(String value);
-
-    /**
-     * Sets the eUICC identifier (EID) of the primary device eSIM. Used by HTTP parameter
-     * "target_terminal_eid" if set.
-     *
-     * <p>Used by primary device ODSA operation.
-     */
-    public abstract Builder setTargetTerminalEid(String value);
-
-    public abstract EsimOdsaOperation build();
-  }
-}
diff --git a/java/com/google/android/libraries/entitlement/ServiceEntitlement.java b/java/com/google/android/libraries/entitlement/ServiceEntitlement.java
deleted file mode 100644
index ea4eec1..0000000
--- a/java/com/google/android/libraries/entitlement/ServiceEntitlement.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 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.google.android.libraries.entitlement;
-
-import java.util.List;
-
-/**
- * Implemnets protocol for carrier service entitlement configuration query and operation, based on
- * GSMA TS.43 spec.
- */
-public class ServiceEntitlement {
-  /** App ID for Voice-Over-LTE entitlement. */
-  public static final String APP_VOLTE = "ap2003";
-  /** App ID for Voice-Over-WiFi entitlement. */
-  public static final String APP_VOWIFI = "ap2004";
-  /** App ID for SMS-Over-IP entitlement. */
-  public static final String APP_SMSOIP = "ap2005";
-  /** App ID for on device service activation (OSDA) for companion device. */
-  public static final String APP_ODSA_COMPANION = "ap2006";
-  /** App ID for on device service activation (OSDA) for primary device. */
-  public static final String APP_ODSA_PRIMARY = "ap2009";
-
-  /**
-   * Creates an instance for service entitlement configuration query and operation for the carrier.
-   *
-   * @param carrierData carrier specific data used in the queries and operations.
-   * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates
-   *     which SIM to retrieve IMEI/IMSI from and perform EAP-AKA authentication with. See {@link
-   *     android.telephony.SubscriptionManager} for how to get the subscroption ID.
-   */
-  public ServiceEntitlement(CarrierData carrierData, int simSubscriptionId) {}
-
-  /**
-   * Retrieves service entitlement configuration. For on device service activation (ODSA) of eSIM
-   * for companion/primary devices, use {@link #performEsimOdsa} instead.
-   *
-   * <p>Supported {@code appId}: {@link #APP_VOLTE}, {@link #APP_VOWIFI}, {@link #APP_SMSOIP}.
-   *
-   * <p>This method sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge
-   * if needed, and returns the raw configuration doc as a string. The following parameters are set
-   * in the HTTP request:
-   *
-   * <ul>
-   *   <li>"app": {@code appId}
-   *   <li>"vers": 0, or {@code request.configurationVersion()} if it's not 0.
-   *   <li>"entitlement_version": "2.0", or {@code request.entitlementVersion()} if it's not empty.
-   *   <li>"token": not set, or {@code request.authenticationToken()} if it's not empty.
-   *   <li>"IMSI": if "token" is set, set to {@link android.telephony.TelephonyManager#getImei}.
-   *   <li>"EAP_ID": if "token" is not set, set this parameter to trigger embedded EAP-AKA
-   *       authentication as decribed in TS.43 section 2.6.1. Its value is derived from IMSI as per
-   *       GSMA spec RCC.14 section C.2.
-   *   <li>"terminal_id": IMEI, or {@code request.terminalId()} if it's not empty.
-   *   <li>"terminal_vendor": {@link android.os.Build#MANUFACTURER}, or {@code
-   *       request.terminalVendor()} if it's not empty.
-   *   <li>"terminal_model": {@link android.os.Build#MODEL}, or {@code request.terminalModel()} if
-   *       it's not empty.
-   *   <li>"terminal_sw_version": {@llink android.os.Build.VERSION#BASE_OS}, or {@code
-   *       request.terminalSoftwareVersion()} if it's not empty.
-   *   <li>"app_name": not set, or {@code request.appName()} if it's not empty.
-   *   <li>"app_version": not set, or {@code request.appVersion()} if it's not empty.
-   *   <li>"notif_token": not set, or {@code request.notificationToken()} if it's not empty.
-   *   <li>"notif_action": {@code request.notificationAction()} if "notif_token" is set, otherwise
-   *       not set.
-   * </ul>
-   *
-   * <p>Requires permission: READ_PRIVILEGED_PHONE_STATE, or carrier privilege.
-   *
-   * @param appId an app ID string defined in TS.43 section 2.2, e.g. {@link #APP_VOWIFI}.
-   * @param request contains parameters that can be used in the HTTP request.
-   */
-  public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request)
-      throws ServiceEntitlementException {
-    // TODO(samalin): Add implementation
-    return null;
-  }
-
-  /**
-   * Retrieves service entitlement configurations for multiple app IDs in one HTTP request/response.
-   * For on device service activation (ODSA) of eSIM for companion/primary devices, use {@link
-   * #performEsimOdsa} instead.
-   *
-   * <p>Same with {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that
-   * multiple "app" parameters will be set in the HTTP request, in the order as they appear in
-   * parameter {@code appIds}.
-   */
-  public String queryEntitlementStatus(List<String> appIds, ServiceEntitlementRequest request)
-      throws ServiceEntitlementException {
-    // TODO(samalin): Add implementation
-    return null;
-  }
-
-  /**
-   * Performs on device service activation (ODSA) of eSIM for companion/primary devices.
-   *
-   * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}.
-   *
-   * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this method
-   * sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if needed, and
-   * returns the raw configuration doc as a string. Additional parameters from {@code operation}
-   * are set to the HTTP request. See {@link EsimOdsaOperation} for details.
-   */
-  public String performEsimOdsa(
-      String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)
-      throws ServiceEntitlementException {
-    // TODO(samalin): Add implementation
-    return null;
-  }
-}
diff --git a/java/com/google/android/libraries/entitlement/ServiceEntitlementException.java b/java/com/google/android/libraries/entitlement/ServiceEntitlementException.java
deleted file mode 100644
index b632e2f..0000000
--- a/java/com/google/android/libraries/entitlement/ServiceEntitlementException.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 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.google.android.libraries.entitlement;
-
-/** Indicates errors happened in retrieving service entitlement configuration. */
-public class ServiceEntitlementException extends Exception {
-  /** Unknown error. */
-  public static final int ERROR_UNKNOWN = 0;
-  /** Android telephony is unable to provide info like IMSI, e.g. when modem crashed. */
-  public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
-  /**
-   * SIM not returning a response to the EAP-AKA challenge, e.g. when the challenge is invalid. This
-   * can happen only when an embedded EAP-AKA challange is conducted, as per GMSA spec TS.43 section
-   * 2.6.1.
-   */
-  public static final int ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE = 2;
-  /**
-   * Cannot connect to the entitlment server, e.g. due to weak mobile network and Wi-Fi connection.
-   */
-  public static final int ERROR_SEVER_NOT_CONNECTABLE = 3;
-  /**
-   * HTTP response received with a status code indicating failure, e.g. 4xx and 5xx. Use {@link
-   * #getHttpStatus} to get the status code and {@link #getMessage} the error message in the
-   * response body.
-   */
-  public static final int ERROR_HTTP_STATUS_NOT_SUCCESS = 4;
-
-  public ServiceEntitlementException(
-      int error, int httpStatus, String retryAfter, String message, Throwable cause) {}
-
-  /** Returns the error code, see {@link #ERROR_*}. */
-  public int getErrorCode() {
-    // TODO(samalin): add implementation
-    return ERROR_UNKNOWN;
-  }
-  /** Returns the HTTP status code returned by entitlement server; 0 if unavailable. */
-  public int getHttpStatus() {
-    // TODO(samalin): add implementation
-    return ERROR_SEVER_NOT_CONNECTABLE;
-  }
-  /**
-   * Returns the "Retry-After" header in HTTP response, often set with HTTP status code 503; an
-   * empty string if unavailable.
-   *
-   * @return the HTTP-date or a number of seconds to delay, as defiend in RFC 7231:
-   * https://tools.ietf.org/html/rfc7231#section-7.1.3
-   */
-  public String getRetryAfter() {
-    // TODO(samalin): add implementation
-    return null;
-  }
-}
diff --git a/java/com/google/android/libraries/entitlement/ServiceEntitlementRequest.java b/java/com/google/android/libraries/entitlement/ServiceEntitlementRequest.java
deleted file mode 100644
index 032579c..0000000
--- a/java/com/google/android/libraries/entitlement/ServiceEntitlementRequest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2020 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.google.android.libraries.entitlement;
-
-import com.google.auto.value.AutoValue;
-
-/** Service entitlement HTTP request parameters, as defiend in GSMA spec TS.43 section 2.2. */
-@AutoValue
-public abstract class ServiceEntitlementRequest {
-  /** Disables notification token. */
-  public static final int NOTICATION_ACTION_DISABLE = 0;
-  /** Enables FCM notification token. */
-  public static final int NOTICATION_ACTION_ENABLE_FCM = 2;
-
-  /**
-   * Returns the version of configuration currently stored on the client. Used by HTTP parameter
-   * "vers".
-   */
-  public abstract int configurationVersion();
-
-  /**
-   * Returns the version of the entitlement specification. Used by HTTP parameter
-   * "entitlement_version".
-   */
-  public abstract String entitlementVersion();
-
-  /** Returns the authentication token. Used by HTTP parameter "token". */
-  public abstract String authenticationToken();
-
-  /**
-   * Returns the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id".
-   */
-  public abstract String terminalId();
-
-  /** Returns the OEM of the device. Used by HTTP parameter "terminal_vendor". */
-  public abstract String terminalVendor();
-
-  /** Returns the model of the device. Used by HTTP parameter "terminal_model". */
-  public abstract String terminalModel();
-
-  /** Returns the software version of the device. Used by HTTP parameter "terminal_sw_version". */
-  public abstract String terminalSoftwareVersion();
-
-  /**
-   * Returns the name of the device application making the request. Used by HTTP parameter
-   * "app_name".
-   */
-  public abstract String appName();
-
-  /**
-   * Returns the version of the device application making the request. Used by HTTP parameter
-   * "app_version".
-   */
-  public abstract String appVersion();
-
-  /**
-   * Returns the FCM registration token used to register for entitlement configuration request from
-   * network. Used by HTTP parameter "notif_token".
-   */
-  public abstract String notificationToken();
-
-  /**
-   * Returns the action associated with the FCM registration token. Used by HTTP parameter
-   * "notif_action".
-   *
-   * @see #NOTICATION_ACTION_ENABLE_FCM
-   * @see #NOTICATION_ACTION_DISABLE
-   */
-  public abstract int notificationAction();
-
-  /** Returns a new {@link Builder} object. */
-  public static Builder builder() {
-    return new AutoValue_ServiceEntitlementRequest.Builder()
-        .setConfigurationVersion(0)
-        .setEntitlementVersion("2.0")
-        .setNotificationAction(NOTICATION_ACTION_ENABLE_FCM);
-  }
-
-  /** Builder. */
-  @AutoValue.Builder
-  public abstract static class Builder {
-    /**
-     * Sets the version of configuration currently stored on the client. Used by HTTP parameter
-     * "vers".
-     *
-     * <p>If not set, default to 0 indicating no existing configuration.
-     */
-    public abstract Builder setConfigurationVersion(int value);
-    /**
-     * Sets the version of configuration currently stored on the client. Used by HTTP parameter
-     * "vers".
-     *
-     * <p>If not set, default to "2.0".
-     */
-    public abstract Builder setEntitlementVersion(String value);
-    /**
-     * Sets the authentication token. Used by HTTP parameter "token".
-     *
-     * <p>If not set, will trigger embedded EAP-AKA authentication as decribed in TS.43 section
-     * 2.6.1.
-     */
-    public abstract Builder setAuthenticationToken(String value);
-    /**
-     * Sets the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id".
-     *
-     * <p>If not set, will use the device IMEI.
-     */
-    public abstract Builder setTerminalId(String value);
-    /**
-     * Sets the OEM of the device. Used by HTTP parameter "terminal_vendor".
-     *
-     * <p>If not set, will use {@link android.os.Build#MANUFACTURER}.
-     */
-    public abstract Builder setTerminalVendor(String value);
-    /**
-     * Sets the model of the device. Used by HTTP parameter "terminal_model".
-     *
-     * <p>If not set, will use {@link android.os.Build#MODEL}.
-     */
-    public abstract Builder setTerminalModel(String value);
-    /**
-     * Sets the software version of the device. Used by HTTP parameter "terminal_sw_version".
-     *
-     * <p>If not set, will use {@link android.os.Build.VERSION#BASE_OS}.
-     */
-    public abstract Builder setTerminalSoftwareVersion(String value);
-    /**
-     * Sets the name of the device application making the request. Used by HTTP parameter
-     * "app_name".
-     *
-     * <p>Optional.
-     */
-    public abstract Builder setAppName(String value);
-    /**
-     * Sets the version of the device application making the request. Used by HTTP parameter
-     * "app_version".
-     *
-     * <p>Optional.
-     */
-    public abstract Builder setAppVersion(String value);
-    /**
-     * Sets the FCM registration token used to register for entitlement configuration request from
-     * network. Used by HTTP parameter "notif_token".
-     *
-     * <p>Optional.
-     */
-    public abstract Builder setNotificationToken(String value);
-    /**
-     * Sets the action associated with the FCM registration token. Used by HTTP parameter
-     * "notif_action".
-     *
-     * <p>Required if a token is set with {@link #setNotificationToken}, and default to {@link
-     * #NOTICATION_ACTION_ENABLE_FCM}; otherwise ignored.
-     *
-     * @see #NOTICATION_ACTION_ENABLE_FCM
-     * @see #NOTICATION_ACTION_DISABLE
-     */
-    public abstract Builder setNotificationAction(int value);
-
-    public abstract ServiceEntitlementRequest build();
-  }
-}