Implement for EsimOsdaOprations

Bug: 177544547
Test: unit tests
Change-Id: I0fe91dadc6af7d1a6a72b96d50d0b731586e980d
(cherry picked from commit b48a7e91915dc87faeea10ce3b7e0c4c8008ca11)
diff --git a/java/com/android/libraries/entitlement/EsimOdsaOperation.java b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
index ce764f2..20f4fa3 100644
--- a/java/com/android/libraries/entitlement/EsimOdsaOperation.java
+++ b/java/com/android/libraries/entitlement/EsimOdsaOperation.java
@@ -44,7 +44,7 @@
     /**
      * Indicates that operation_type is not set.
      */
-    static final int OPERATION_TYPE_NOT_SET = -1;
+    public static final int OPERATION_TYPE_NOT_SET = -1;
     /**
      * To activate a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}.
      */
@@ -138,13 +138,12 @@
     public abstract String companionTerminalIccid();
 
     /**
-     * Returns the ICCID of the companion device. Used by HTTP parameter
-     * "companion_terminal_iccid".
+     * Returns the EID of the companion device. Used by HTTP parameter "companion_terminal_eid".
      */
     public abstract String companionTerminalEid();
 
     /**
-     * Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid".
+     * Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_iccid".
      */
     public abstract String terminalIccid();
 
@@ -175,7 +174,22 @@
      * Returns a new {@link Builder} object.
      */
     public static Builder builder() {
-        return new AutoValue_EsimOdsaOperation.Builder().setOperationType(OPERATION_TYPE_NOT_SET);
+        return new AutoValue_EsimOdsaOperation.Builder()
+                .setOperation("")
+                .setOperationType(OPERATION_TYPE_NOT_SET)
+                .setCompanionTerminalId("")
+                .setCompanionTerminalVendor("")
+                .setCompanionTerminalModel("")
+                .setCompanionTerminalSoftwareVersion("")
+                .setCompanionTerminalFriendlyName("")
+                .setCompanionTerminalService("")
+                .setCompanionTerminalIccid("")
+                .setCompanionTerminalEid("")
+                .setTerminalIccid("")
+                .setTerminalEid("")
+                .setTargetTerminalId("")
+                .setTargetTerminalIccid("")
+                .setTargetTerminalEid("");
     }
 
     /**
@@ -283,39 +297,40 @@
         public abstract Builder setCompanionTerminalEid(String value);
 
         /**
-         * Sets the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid" if set.
+         * Sets the ICCID of the primary device eSIM in case of primary SIM not present. 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.
+         * Sets the eUICC identifier (EID) of the primary device eSIM in case of primary SIM not
+         * present. 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.
+         * Sets the unique identifier of the primary device eSIM in case of multiple SIM, 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.
+         * Sets the ICCID primary device eSIM in case of multiple SIM. 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.
+         * Sets the eUICC identifier (EID) of the primary device eSIM in case of multiple SIM. Used
+         * by HTTP parameter "target_terminal_eid" if set.
          *
          * <p>Used by primary device ODSA operation.
          */
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java
index 16aeeaa..efb0734 100644
--- a/java/com/android/libraries/entitlement/ServiceEntitlement.java
+++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java
@@ -152,7 +152,6 @@
     public String performEsimOdsa(
             String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation)
             throws ServiceEntitlementException {
-        // TODO(b/177544547): Add implementation
-        return null;
+        return eapAkaApi.performEsimOdsaOperation(appId, carrierConfig, request, operation);
     }
 }
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
index dd15426..bf53d87 100644
--- a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
@@ -29,6 +29,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.libraries.entitlement.CarrierConfig;
+import com.android.libraries.entitlement.EsimOdsaOperation;
 import com.android.libraries.entitlement.ServiceEntitlementException;
 import com.android.libraries.entitlement.ServiceEntitlementRequest;
 import com.android.libraries.entitlement.http.HttpClient;
@@ -48,69 +49,45 @@
 
     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";
-    /**
-     * Expected content type of the response body.
-     */
+
     private static final String ACCEPT_CONTENT_TYPE_JSON_AND_XML =
             "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap.connectivity-xml";
-    /**
-     * Required content type to the response body.
-     */
     private static final String REQUEST_CONTENT_TYPE_JSON =
             "application/vnd.gsma.eap-relay.v1.0+json";
 
+    private static final String OPERATION = "operation";
+    private static final String OPERATION_TYPE = "operation_type";
+    private static final String COMPANION_TERMINAL_ID = "companion_terminal_id";
+    private static final String COMPANION_TERMINAL_VENDOR = "companion_terminal_vendor";
+    private static final String COMPANION_TERMINAL_MODEL = "companion_terminal_model";
+    private static final String COMPANION_TERMINAL_SW_VERSION = "companion_terminal_sw_version";
+    private static final String COMPANION_TERMINAL_FRIENDLY_NAME =
+            "companion_terminal_friendly_name";
+    private static final String COMPANION_TERMINAL_SERVICE = "companion_terminal_service";
+    private static final String COMPANION_TERMINAL_ICCID = "companion_terminal_iccid";
+    private static final String COMPANION_TERMINAL_EID = "companion_terminal_eid";
+
+    private static final String TERMINAL_ICCID = "terminal_iccid";
+    private static final String TERMINAL_EID = "terminal_eid";
+
+    private static final String TARGET_TERMINAL_ID = "target_terminal_id";
+    private static final String TARGET_TERMINAL_ICCID = "target_terminal_iccid";
+    private static final String TARGET_TERMINAL_EID = "target_terminal_eid";
+
     // In case of EAP-AKA synchronization failure, we try to recover for at most two times.
     private static final int FOLLOW_SYNC_FAILURE_MAX_COUNT = 2;
 
@@ -138,18 +115,11 @@
      */
     @Nullable
     public String queryEntitlementStatus(ImmutableList<String> appIds,
-                                         CarrierConfig carrierConfig,
-                                         ServiceEntitlementRequest request)
+            CarrierConfig carrierConfig, ServiceEntitlementRequest request)
             throws ServiceEntitlementException {
-        HttpRequest httpRequest =
-                HttpRequest.builder()
-                        .setUrl(entitlementStatusUrl(appIds, carrierConfig.serverUrl(), request))
-                        .setRequestMethod(RequestMethod.GET)
-                        .addRequestProperty(HttpHeaders.ACCEPT, ACCEPT_CONTENT_TYPE_JSON_AND_XML)
-                        .setTimeoutInSec(carrierConfig.timeoutInSec())
-                        .setNetwork(carrierConfig.network())
-                        .build();
-        HttpResponse response = mHttpClient.request(httpRequest);
+        Uri.Builder urlBuilder = Uri.parse(carrierConfig.serverUrl()).buildUpon();
+        appendParametersForServiceEntitlementRequest(urlBuilder, appIds, request);
+        HttpResponse response = httpGet(urlBuilder.toString(), carrierConfig);
         if (!request.authenticationToken().isEmpty()) {
             // Fast Re-Authentication flow with pre-existing auth token
             Log.d(TAG, "Fast Re-Authentication");
@@ -210,8 +180,8 @@
                         carrierConfig, newChallenge, followSyncFailureCount - 1);
             } else {
                 throw new ServiceEntitlementException(
-                    ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE,
-                    "Unable to recover from EAP-AKA synchroinization failure");
+                        ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE,
+                        "Unable to recover from EAP-AKA synchroinization failure");
             }
         } else { // not possible
             throw new AssertionError("EapAkaResponse invalid.");
@@ -243,11 +213,36 @@
         return mHttpClient.request(request);
     }
 
-    String entitlementStatusUrl(
-            ImmutableList<String> appIds, String serverUrl, ServiceEntitlementRequest request) {
+    /**
+     * Retrieves raw doc of performing ODSA operations. For operation type, see {@link
+     * EsimOdsaOperation}.
+     *
+     * <p>Implementation based on GSMA TS.43-v5.0 6.1.
+     */
+    public String performEsimOdsaOperation(String appId, CarrierConfig carrierConfig,
+            ServiceEntitlementRequest request, EsimOdsaOperation odsaOperation)
+            throws ServiceEntitlementException {
+        Uri.Builder urlBuilder = Uri.parse(carrierConfig.serverUrl()).buildUpon();
+        appendParametersForServiceEntitlementRequest(urlBuilder, ImmutableList.of(appId), request);
+        appendParametersForEsimOdsaOperation(urlBuilder, odsaOperation);
+
+        HttpResponse response = httpGet(urlBuilder.toString(), carrierConfig);
+        if (!request.authenticationToken().isEmpty()) {
+            // Fast Re-Authentication flow with pre-existing auth token
+            Log.d(TAG, "Fast Re-Authentication");
+            return response.body();
+        }
+        // Full Authentication flow
+        Log.d(TAG, "Full Authentication");
+        return respondToEapAkaChallenge(carrierConfig, response, FOLLOW_SYNC_FAILURE_MAX_COUNT)
+                .body();
+    }
+
+    private void appendParametersForServiceEntitlementRequest(
+            Uri.Builder urlBuilder, ImmutableList<String> appIds,
+            ServiceEntitlementRequest request) {
         TelephonyManager telephonyManager = mContext.getSystemService(
                 TelephonyManager.class).createForSubscriptionId(mSimSubscriptionId);
-        Uri.Builder urlBuilder = Uri.parse(serverUrl).buildUpon();
         if (TextUtils.isEmpty(request.authenticationToken())) {
             // EAP_ID required for initial AuthN
             urlBuilder.appendQueryParameter(
@@ -276,27 +271,74 @@
         }
 
         // 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());
-        }
+        appendOptionalQueryParameter(urlBuilder, APP_VERSION, request.appVersion());
+        appendOptionalQueryParameter(urlBuilder, APP_NAME, request.appName());
 
         for (String appId : appIds) {
             urlBuilder.appendQueryParameter(APP, appId);
         }
 
-        return urlBuilder
+        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(VERS, Integer.toString(request.configurationVersion()))
-                .appendQueryParameter(ENTITLEMENT_VERSION, request.entitlementVersion())
-                .toString();
+                .appendQueryParameter(ENTITLEMENT_VERSION, request.entitlementVersion());
+    }
+
+    private void appendParametersForEsimOdsaOperation(
+            Uri.Builder urlBuilder, EsimOdsaOperation odsaOperation) {
+        urlBuilder.appendQueryParameter(OPERATION, odsaOperation.operation());
+        if (odsaOperation.operationType() != EsimOdsaOperation.OPERATION_TYPE_NOT_SET) {
+            urlBuilder.appendQueryParameter(OPERATION_TYPE,
+                    Integer.toString(odsaOperation.operationType()));
+        }
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_ID,
+                odsaOperation.companionTerminalId());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_VENDOR,
+                odsaOperation.companionTerminalVendor());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_MODEL,
+                odsaOperation.companionTerminalModel());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_SW_VERSION,
+                odsaOperation.companionTerminalSoftwareVersion());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_FRIENDLY_NAME,
+                odsaOperation.companionTerminalFriendlyName());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_SERVICE,
+                odsaOperation.companionTerminalService());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_ICCID,
+                odsaOperation.companionTerminalIccid());
+        appendOptionalQueryParameter(urlBuilder, COMPANION_TERMINAL_EID,
+                odsaOperation.companionTerminalEid());
+        appendOptionalQueryParameter(urlBuilder, TERMINAL_ICCID,
+                odsaOperation.terminalIccid());
+        appendOptionalQueryParameter(urlBuilder, TERMINAL_EID, odsaOperation.terminalEid());
+        appendOptionalQueryParameter(urlBuilder, TARGET_TERMINAL_ID,
+                odsaOperation.targetTerminalId());
+        appendOptionalQueryParameter(urlBuilder, TARGET_TERMINAL_ICCID,
+                odsaOperation.targetTerminalIccid());
+        appendOptionalQueryParameter(urlBuilder, TARGET_TERMINAL_EID,
+                odsaOperation.targetTerminalEid());
+    }
+
+    private HttpResponse httpGet(String url, CarrierConfig carrierConfig)
+            throws ServiceEntitlementException {
+        HttpRequest httpRequest =
+                HttpRequest.builder()
+                        .setUrl(url)
+                        .setRequestMethod(RequestMethod.GET)
+                        .addRequestProperty(HttpHeaders.ACCEPT, ACCEPT_CONTENT_TYPE_JSON_AND_XML)
+                        .setTimeoutInSec(carrierConfig.timeoutInSec())
+                        .setNetwork(carrierConfig.network())
+                        .build();
+        return mHttpClient.request(httpRequest);
+    }
+
+    private void appendOptionalQueryParameter(Uri.Builder urlBuilder, String key, String value) {
+        if (!TextUtils.isEmpty(value)) {
+            urlBuilder.appendQueryParameter(key, value);
+        }
     }
 
     /**
diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java
index cc80ea0..99e8cfa 100644
--- a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java
+++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java
@@ -38,6 +38,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.libraries.entitlement.CarrierConfig;
+import com.android.libraries.entitlement.EsimOdsaOperation;
 import com.android.libraries.entitlement.ServiceEntitlement;
 import com.android.libraries.entitlement.ServiceEntitlementException;
 import com.android.libraries.entitlement.ServiceEntitlementRequest;
@@ -92,13 +93,19 @@
     private static final String ACCEPT_CONTENT_TYPE_JSON_AND_XML =
             "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap.connectivity-xml";
 
-    @Rule public final MockitoRule rule = MockitoJUnit.rule();
+    @Rule
+    public final MockitoRule rule = MockitoJUnit.rule();
 
-    @Mock private HttpClient mMockHttpClient;
-    @Mock private Network mMockNetwork;
-    @Mock private TelephonyManager mMockTelephonyManager;
-    @Mock private TelephonyManager mMockTelephonyManagerForSubId;
-    @Captor private ArgumentCaptor<HttpRequest> mHttpRequestCaptor;
+    @Mock
+    private HttpClient mMockHttpClient;
+    @Mock
+    private Network mMockNetwork;
+    @Mock
+    private TelephonyManager mMockTelephonyManager;
+    @Mock
+    private TelephonyManager mMockTelephonyManagerForSubId;
+    @Captor
+    private ArgumentCaptor<HttpRequest> mHttpRequestCaptor;
 
     private Context mContext;
     private EapAkaApi mEapAkaApi;
@@ -117,20 +124,20 @@
 
     @Test
     public void queryEntitlementStatus_hasAuthenticationToken() throws Exception {
-        HttpResponse response =
+        HttpResponse httpResponse =
                 HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML)
                         .build();
-        when(mMockHttpClient.request(any())).thenReturn(response);
+        when(mMockHttpClient.request(any())).thenReturn(httpResponse);
         CarrierConfig carrierConfig =
                 CarrierConfig.builder().setServerUrl(TEST_URL).setNetwork(mMockNetwork).build();
         ServiceEntitlementRequest request =
                 ServiceEntitlementRequest.builder().setAuthenticationToken(TOKEN).build();
 
-        String respopnse =
+        String response =
                 mEapAkaApi.queryEntitlementStatus(
                         ImmutableList.of(ServiceEntitlement.APP_VOWIFI), carrierConfig, request);
 
-        assertThat(respopnse).isEqualTo(RESPONSE_XML);
+        assertThat(response).isEqualTo(RESPONSE_XML);
         verify(mMockHttpClient).request(mHttpRequestCaptor.capture());
         assertThat(mHttpRequestCaptor.getValue().timeoutInSec())
                 .isEqualTo(CarrierConfig.DEFAULT_TIMEOUT_IN_SEC);
@@ -208,7 +215,7 @@
 
         ServiceEntitlementException exception = expectThrows(
                 ServiceEntitlementException.class,
-                () ->  mEapAkaApi.queryEntitlementStatus(
+                () -> mEapAkaApi.queryEntitlementStatus(
                         ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
                         carrierConfig,
                         request));
@@ -230,7 +237,7 @@
 
         ServiceEntitlementException exception = expectThrows(
                 ServiceEntitlementException.class,
-                () ->  mEapAkaApi.queryEntitlementStatus(
+                () -> mEapAkaApi.queryEntitlementStatus(
                         ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
                         carrierConfig,
                         request));
@@ -276,4 +283,55 @@
         assertThat(mHttpRequestCaptor.getAllValues().get(2).requestProperties())
                 .containsEntry(HTTP_HEADER_COOKIE, COOKIE_VALUE);
     }
+
+    @Test
+    public void performEsimOdsaOperation_noAuthenticationToken_returnsResult() throws Exception {
+        when(mMockTelephonyManagerForSubId.getIccAuthentication(
+                TelephonyManager.APPTYPE_USIM,
+                TelephonyManager.AUTHTYPE_EAP_AKA,
+                EAP_AKA_SECURITY_CONTEXT_REQUEST_EXPECTED))
+                .thenReturn(EAP_AKA_SECURITY_CONTEXT_RESPONSE_SUCCESS);
+        HttpResponse eapChallengeResponse =
+                HttpResponse
+                        .builder().setContentType(ContentType.JSON).setBody(EAP_AKA_CHALLENGE)
+                        .setCookie(COOKIE_VALUE).build();
+        HttpResponse xmlResponse =
+                HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML)
+                        .build();
+        when(mMockHttpClient.request(any()))
+                .thenReturn(eapChallengeResponse).thenReturn(xmlResponse);
+        CarrierConfig carrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build();
+        ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build();
+        EsimOdsaOperation operation = EsimOdsaOperation.builder().build();
+
+        String response =
+                mEapAkaApi.performEsimOdsaOperation(ServiceEntitlement.APP_ODSA_COMPANION,
+                        carrierConfig, request, operation);
+
+        assertThat(response).isEqualTo(RESPONSE_XML);
+        verify(mMockHttpClient, times(2)).request(any());
+    }
+
+    @Test
+    public void performEsimOdsaOperation_manageSubscription_returnsResult() throws Exception {
+        HttpResponse httpResponse =
+                HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML)
+                        .build();
+        when(mMockHttpClient.request(any())).thenReturn(httpResponse);
+        CarrierConfig carrierConfig =
+                CarrierConfig.builder().setServerUrl(TEST_URL).build();
+        ServiceEntitlementRequest request =
+                ServiceEntitlementRequest.builder().setAuthenticationToken(TOKEN).build();
+        EsimOdsaOperation operation = EsimOdsaOperation.builder()
+                .setOperation(EsimOdsaOperation.OPERATION_MANAGE_SUBSCRIPTION)
+                .setOperationType(EsimOdsaOperation.OPERATION_TYPE_SUBSCRIBE)
+                .build();
+
+        String response =
+                mEapAkaApi.performEsimOdsaOperation(ServiceEntitlement.APP_ODSA_COMPANION,
+                        carrierConfig, request, operation);
+
+        assertThat(response).isEqualTo(RESPONSE_XML);
+        verify(mMockHttpClient, times(1)).request(any());
+    }
 }