diff options
author | Meng Wang <mewan@google.com> | 2021-02-10 19:49:26 -0800 |
---|---|---|
committer | Meng Wang <mewan@google.com> | 2021-02-18 12:57:31 -0800 |
commit | 941c588c6abd615851539467a94ae31da88135ce (patch) | |
tree | b3a73188c4ebba019da1994966627d7166e1374e | |
parent | b7c355416b105a93ee6b7f0f1ae83248184b08cb (diff) | |
download | service_entitlement-941c588c6abd615851539467a94ae31da88135ce.tar.gz |
Allow client to specify timeout and Network
API surface change:
- setTimeoutInSec and setNetwork added to CarrierConfig.Builder
Bug: 175827888
Bug: 177100676
Test: atest
Change-Id: I909fe568898074598d8ae0bd5db90b273534a09d
10 files changed, 294 insertions, 144 deletions
@@ -12,7 +12,20 @@ // 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. + +java_defaults { + name: "service-entitlement-defaults", + libs: [ + "androidx.annotation_annotation", + "auto_value_annotations", + ], + plugins: ["auto_value_plugin"], + sdk_version: "system_current", + min_sdk_version: "29", +} + java_library { + defaults: ["service-entitlement-defaults"], name: "service-entitlement-impl", visibility: [ "//visibility:private" @@ -22,30 +35,23 @@ java_library { "java/com/android/libraries/entitlement/http/*.java", "java/com/android/libraries/entitlement/utils/*.java", ], - libs: [ - "androidx.annotation_annotation", - "auto_value_annotations", - ], static_libs: [ "guava", "service-entitlement-data", ], - plugins: ["auto_value_plugin"], - sdk_version: "system_current", - min_sdk_version: "29", } java_library { + defaults: ["service-entitlement-defaults"], name: "service-entitlement", static_libs: [ "service-entitlement-api", "service-entitlement-data", ], - sdk_version: "system_current", - min_sdk_version: "29", } java_library { + defaults: ["service-entitlement-defaults"], name: "service-entitlement-api", visibility: [ "//visibility:private", @@ -53,22 +59,16 @@ java_library { srcs: [ "java/com/android/libraries/entitlement/ServiceEntitlement.java", ], - libs: [ - "androidx.annotation_annotation", - "auto_value_annotations", - ], static_libs: [ "guava", "service-entitlement-data", "service-entitlement-impl", ], - plugins: ["auto_value_plugin"], - sdk_version: "system_current", - min_sdk_version: "29", } java_library { + defaults: ["service-entitlement-defaults"], name: "service-entitlement-data", visibility: [ "//visibility:private", @@ -79,10 +79,4 @@ java_library { "java/com/android/libraries/entitlement/ServiceEntitlementException.java", "java/com/android/libraries/entitlement/ServiceEntitlementRequest.java", ], - libs: [ - "auto_value_annotations", - ], - plugins: ["auto_value_plugin"], - sdk_version: "current", - min_sdk_version: "29", } diff --git a/java/com/android/libraries/entitlement/CarrierConfig.java b/java/com/android/libraries/entitlement/CarrierConfig.java index 8f040c1..44a4170 100644 --- a/java/com/android/libraries/entitlement/CarrierConfig.java +++ b/java/com/android/libraries/entitlement/CarrierConfig.java @@ -16,6 +16,10 @@ package com.android.libraries.entitlement; +import android.net.Network; + +import androidx.annotation.Nullable; + import com.google.auto.value.AutoValue; /** @@ -25,16 +29,24 @@ import com.google.auto.value.AutoValue; */ @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. - */ + /** Default value of {@link #timeoutInSec} if not set. */ + public static final int DEFAULT_TIMEOUT_IN_SEC = 30; + + /** The carrier's entitlement server URL. See {@link Builder#setServerUrl}. */ public abstract String serverUrl(); + /** Client side timeout for HTTP connection. See {@link Builder#setTimeoutInSec}. */ + public abstract int timeoutInSec(); + + /** The {@link Network} used for HTTP connection. See {@link Builder#setNetwork}. */ + @Nullable + public abstract Network network(); + /** Returns a new {@link Builder} object. */ public static Builder builder() { return new AutoValue_CarrierConfig.Builder() - .setServerUrl(""); + .setServerUrl("") + .setTimeoutInSec(DEFAULT_TIMEOUT_IN_SEC); } /** Builder. */ @@ -43,9 +55,24 @@ public abstract class CarrierConfig { 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. + * Sets 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 TS.43 section 2.1. */ public abstract Builder setServerUrl(String url); + + /** + * Sets the client side timeout for HTTP connection. Default to + * {@link DEFAULT_TIMEOUT_IN_SEC}. + * + * <p>This timeout is used by both {@link java.net.URLConnection#setConnectTimeout} and + * {@link java.net.URLConnection#setReadTimeout}. + */ + public abstract Builder setTimeoutInSec(int timeoutInSec); + + /** + * Sets the {@link Network} used for HTTP connection. If not set, the device default network + * is used. + */ + public abstract Builder setNetwork(Network network); } } diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java index 77cc5ae..16aeeaa 100644 --- a/java/com/android/libraries/entitlement/ServiceEntitlement.java +++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java @@ -121,8 +121,7 @@ public class ServiceEntitlement { @Nullable public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request) throws ServiceEntitlementException { - return eapAkaApi.queryEntitlementStatus(ImmutableList.of(appId), carrierConfig.serverUrl(), - request); + return eapAkaApi.queryEntitlementStatus(ImmutableList.of(appId), carrierConfig, request); } /** @@ -137,7 +136,7 @@ public class ServiceEntitlement { public String queryEntitlementStatus(ImmutableList<String> appIds, ServiceEntitlementRequest request) throws ServiceEntitlementException { - return eapAkaApi.queryEntitlementStatus(appIds, carrierConfig.serverUrl(), request); + return eapAkaApi.queryEntitlementStatus(appIds, carrierConfig, request); } /** diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java index 4ad0332..86fd0c5 100644 --- a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java +++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java @@ -27,6 +27,7 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.libraries.entitlement.CarrierConfig; import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.ServiceEntitlementRequest; import com.android.libraries.entitlement.http.HttpClient; @@ -132,14 +133,17 @@ public class EapAkaApi { * @throws ServiceEntitlementException when getting an unexpected http response. */ @Nullable - public String queryEntitlementStatus( - ImmutableList<String> appIds, String serverUrl, ServiceEntitlementRequest request) + public String queryEntitlementStatus(ImmutableList<String> appIds, + CarrierConfig carrierConfig, + ServiceEntitlementRequest request) throws ServiceEntitlementException { HttpRequest httpRequest = HttpRequest.builder() - .setUrl(entitlementStatusUrl(appIds, serverUrl, request)) + .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); if (request.authenticationToken().isEmpty()) { @@ -160,7 +164,7 @@ public class EapAkaApi { } return challengeResponse( new EapAkaResponse(responseData).getEapAkaChallengeResponse(mContext, - mSimSubscriptionId), serverUrl, response.cookie()); + mSimSubscriptionId), carrierConfig, response.cookie()); } else { // Result of fast AuthN Log.d(TAG, "fast AuthN"); @@ -168,7 +172,9 @@ public class EapAkaApi { } } - private String challengeResponse(String akaChallengeResponse, String serverUrl, String cookie) + private String challengeResponse(String akaChallengeResponse, + CarrierConfig carrierConfig, + String cookie) throws ServiceEntitlementException { Log.d(TAG, "challengeResponse"); JSONObject postData = new JSONObject(); @@ -180,12 +186,14 @@ public class EapAkaApi { } HttpRequest request = HttpRequest.builder() - .setUrl(serverUrl) + .setUrl(carrierConfig.serverUrl()) .setRequestMethod(RequestMethod.POST) .setPostData(postData) .addRequestProperty(HttpHeaders.ACCEPT, ACCEPT_CONTENT_TYPE_JSON_AND_XML) .addRequestProperty(HttpHeaders.CONTENT_TYPE, REQUEST_CONTENT_TYPE_JSON) .addRequestProperty(HttpHeaders.COOKIE, cookie) + .setTimeoutInSec(carrierConfig.timeoutInSec()) + .setNetwork(carrierConfig.network()) .build(); return mHttpClient.request(request).body(); } diff --git a/java/com/android/libraries/entitlement/http/HttpClient.java b/java/com/android/libraries/entitlement/http/HttpClient.java index 1144c5a..f88a42c 100644 --- a/java/com/android/libraries/entitlement/http/HttpClient.java +++ b/java/com/android/libraries/entitlement/http/HttpClient.java @@ -27,6 +27,7 @@ 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.net.Network; import android.text.TextUtils; import android.util.Log; @@ -51,9 +52,6 @@ import java.util.Map; public class HttpClient { private static final String TAG = "ServiceEntitlement"; - private static final int SOCKET_TIMEOUT_VALUE = (int) SECONDS.toMillis(30); - private static final int CONNECT_TIMEOUT_VALUE = (int) SECONDS.toMillis(30); - private HttpURLConnection mConnection; @WorkerThread @@ -85,7 +83,12 @@ public class HttpClient { private void createConnection(HttpRequest request) throws ServiceEntitlementException { try { URL url = new URL(request.url()); - mConnection = (HttpURLConnection) url.openConnection(); + Network network = request.network(); + if (network == null) { + mConnection = (HttpURLConnection) url.openConnection(); + } else { + mConnection = (HttpURLConnection) network.openConnection(url); + } // add HTTP headers for (Map.Entry<String, String> entry : request.requestProperties().entrySet()) { @@ -94,8 +97,8 @@ public class HttpClient { // set parameters mConnection.setRequestMethod(request.requestMethod()); - mConnection.setConnectTimeout(CONNECT_TIMEOUT_VALUE); - mConnection.setReadTimeout(SOCKET_TIMEOUT_VALUE); + mConnection.setConnectTimeout((int) SECONDS.toMillis(request.timeoutInSec())); + mConnection.setReadTimeout((int) SECONDS.toMillis(request.timeoutInSec())); if (POST.equals(request.requestMethod())) { mConnection.setDoOutput(true); } diff --git a/java/com/android/libraries/entitlement/http/HttpRequest.java b/java/com/android/libraries/entitlement/http/HttpRequest.java index 4a56a60..af567db 100644 --- a/java/com/android/libraries/entitlement/http/HttpRequest.java +++ b/java/com/android/libraries/entitlement/http/HttpRequest.java @@ -16,8 +16,13 @@ package com.android.libraries.entitlement.http; +import android.net.Network; import android.util.ArrayMap; +import androidx.annotation.Nullable; + +import com.android.libraries.entitlement.CarrierConfig; + import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableMap; @@ -25,24 +30,32 @@ import org.json.JSONObject; import java.util.Map; -/** - * The parameters of the http request. - */ +/** The parameters of an http request. */ @AutoValue public abstract class HttpRequest { + /** The URL. */ public abstract String url(); + /** The HTTP request method, like "GET" or "POST". */ public abstract String requestMethod(); + /** For "POST" request method, the body of the request in JSON format. */ public abstract JSONObject postData(); + /** For "GET" request method, the parameters to be encoded into the URL. */ public abstract ImmutableMap<String, String> requestValues(); + /** HTTP header fields. */ public abstract ImmutableMap<String, String> requestProperties(); - /** - * Builder of {@link HttpRequest}. - */ + /** The client side timeout, in seconds. See {@link Builder#setTimeoutInSec}. */ + public abstract int timeoutInSec(); + + /** The network used for this HTTP connection. See {@link Builder#setNetwork}. */ + @Nullable + public abstract Network network(); + + /** Builder of {@link HttpRequest}. */ @AutoValue.Builder public abstract static class Builder { private final Map<String, String> values = new ArrayMap<>(); @@ -50,25 +63,49 @@ public abstract class HttpRequest { public abstract HttpRequest build(); + /** Sets the URL. */ public abstract Builder setUrl(String url); + /** + * Sets the HTTP request method, like "GET" or "POST". + * + * @see HttpConstants.RequestMethod + */ public abstract Builder setRequestMethod(String requestMethod); + /** For "POST" request method, sets the body of the request in JSON format. */ public abstract Builder setPostData(JSONObject postData); abstract Builder setRequestValues(ImmutableMap<String, String> value); abstract Builder setRequestProperties(ImmutableMap<String, String> properties); + /** For "GET" request method, adds a parameter to be encoded into the URL. */ public Builder addRequestValues(String key, String value) { values.put(key, value); return this.setRequestValues(ImmutableMap.copyOf(values)); } + /** Adds an HTTP header field. */ public Builder addRequestProperty(String key, String value) { properties.put(key, value); return this.setRequestProperties(ImmutableMap.copyOf(properties)); } + + /** + * Sets the client side timeout for HTTP connection. Default to + * {@link com.android.libraries.entitlement.CarrierConfig#DEFAULT_TIMEOUT_IN_SEC}. + * + * <p>This timeout is used by both {@link java.net.URLConnection#setConnectTimeout} and + * {@link java.net.URLConnection#setReadTimeout}. + */ + public abstract Builder setTimeoutInSec(int timeoutInSec); + + /** + * Sets the network used for this HTTP connection. If not set, the device default network + * is used. + */ + public abstract Builder setNetwork(@Nullable Network network); } public static Builder builder() { @@ -77,6 +114,7 @@ public abstract class HttpRequest { .setRequestMethod("") .setPostData(new JSONObject()) .setRequestValues(ImmutableMap.of()) - .setRequestProperties(ImmutableMap.of()); + .setRequestProperties(ImmutableMap.of()) + .setTimeoutInSec(CarrierConfig.DEFAULT_TIMEOUT_IN_SEC); } } diff --git a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java index 410375d..c9f9b06 100644 --- a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java +++ b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java @@ -61,9 +61,8 @@ public class ServiceEntitlementTest { @Test public void queryEntitlementStatus_appVolte_returnResult() throws Exception { ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); - when(mMockEapAkaApi.queryEntitlementStatus(ImmutableList.of(ServiceEntitlement.APP_VOLTE), - TEST_URL, - request)) + when(mMockEapAkaApi.queryEntitlementStatus( + ImmutableList.of(ServiceEntitlement.APP_VOLTE), mCarrierConfig, request)) .thenReturn(QUERY_APP_VOLTE_RESULT); assertThat( @@ -74,9 +73,8 @@ public class ServiceEntitlementTest { @Test public void queryEntitlementStatus_appVowifi_returnResult() throws Exception { ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); - when(mMockEapAkaApi.queryEntitlementStatus(ImmutableList.of(ServiceEntitlement.APP_VOWIFI), - TEST_URL, - request)) + when(mMockEapAkaApi.queryEntitlementStatus( + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), mCarrierConfig, request)) .thenReturn(QUERY_APP_VOWIFI_RESULT); assertThat( diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java index 4d4f49f..62297a7 100644 --- a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java +++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java @@ -23,14 +23,16 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.testng.Assert.fail; +import static org.testng.Assert.expectThrows; import android.content.Context; +import android.net.Network; import android.telephony.TelephonyManager; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; +import com.android.libraries.entitlement.CarrierConfig; import com.android.libraries.entitlement.ServiceEntitlement; import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.ServiceEntitlementRequest; @@ -95,6 +97,7 @@ public class EapAkaApiTest { @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; @@ -110,6 +113,13 @@ public class EapAkaApiTest { .thenReturn(mMockTelephonyManager); when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) .thenReturn(mMockTelephonyManagerForSubId); + when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn(IMSI); + when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn(MCCMNC); + when(mMockTelephonyManagerForSubId.getIccAuthentication( + TelephonyManager.APPTYPE_USIM, + TelephonyManager.AUTHTYPE_EAP_AKA, + GSM_SECURITY_CONTEXT_REQUEST)) + .thenReturn(GSM_SECURITY_CONTEXT_RESPONSE); } @Test @@ -118,26 +128,24 @@ public class EapAkaApiTest { HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML) .build(); when(mMockHttpClient.request(any())).thenReturn(response); + CarrierConfig carrierConfig = + CarrierConfig.builder().setServerUrl(TEST_URL).setNetwork(mMockNetwork).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().setAuthenticationToken(TOKEN).build(); String respopnse = mEapAkaApi.queryEntitlementStatus( - ImmutableList.of(ServiceEntitlement.APP_VOWIFI), TEST_URL, request); + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), carrierConfig, request); assertThat(respopnse).isEqualTo(RESPONSE_XML); verify(mMockHttpClient).request(mHttpRequestCaptor.capture()); + assertThat(mHttpRequestCaptor.getValue().timeoutInSec()) + .isEqualTo(CarrierConfig.DEFAULT_TIMEOUT_IN_SEC); + assertThat(mHttpRequestCaptor.getValue().network()).isEqualTo(mMockNetwork); } @Test public void queryEntitlementStatus_noAuthenticationToken() throws Exception { - when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn(IMSI); - when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn(MCCMNC); - when(mMockTelephonyManagerForSubId.getIccAuthentication( - TelephonyManager.APPTYPE_USIM, - TelephonyManager.AUTHTYPE_EAP_AKA, - GSM_SECURITY_CONTEXT_REQUEST)) - .thenReturn(GSM_SECURITY_CONTEXT_RESPONSE); HttpResponse eapChallengeResponse = HttpResponse .builder().setContentType(ContentType.JSON).setBody(EAP_AKA_CHALLENGE) @@ -147,80 +155,91 @@ public class EapAkaApiTest { .build(); when(mMockHttpClient.request(any())) .thenReturn(eapChallengeResponse).thenReturn(xmlResponse); + CarrierConfig carrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); String respopnse = mEapAkaApi.queryEntitlementStatus( - ImmutableList.of(ServiceEntitlement.APP_VOWIFI), TEST_URL, request); + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), carrierConfig, request); assertThat(respopnse).isEqualTo(RESPONSE_XML); // Verify that the 2nd request has cookie set by the 1st response verify(mMockHttpClient, times(2)).request(mHttpRequestCaptor.capture()); assertThat(mHttpRequestCaptor.getAllValues().get(1).requestProperties()) .containsEntry(HTTP_HEADER_COOKIE, COOKIE_VALUE); + assertThat(mHttpRequestCaptor.getAllValues().get(0).timeoutInSec()) + .isEqualTo(CarrierConfig.DEFAULT_TIMEOUT_IN_SEC); + assertThat(mHttpRequestCaptor.getAllValues().get(0).network()).isNull(); + assertThat(mHttpRequestCaptor.getAllValues().get(1).timeoutInSec()) + .isEqualTo(CarrierConfig.DEFAULT_TIMEOUT_IN_SEC); + assertThat(mHttpRequestCaptor.getAllValues().get(1).network()).isNull(); } @Test - public void queryEntitlementStatus_multipleAppIds_verifyEntitlementUrl() throws Exception { - when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn(IMSI); - when(mMockTelephonyManagerForSubId.getImei()).thenReturn(IMEI); + public void queryEntitlementStatus_hasAuthenticationToken_multipleAppIds() throws Exception { HttpResponse response = HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML) .build(); when(mMockHttpClient.request(any())).thenReturn(response); - ImmutableList<String> appIds = ImmutableList.of(ServiceEntitlement.APP_VOWIFI, ServiceEntitlement.APP_VOLTE); + CarrierConfig carrierConfig = + CarrierConfig.builder().setServerUrl(TEST_URL).setTimeoutInSec(70).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().setAuthenticationToken(TOKEN).build(); - mEapAkaApi.queryEntitlementStatus(appIds, TEST_URL, request); + + mEapAkaApi.queryEntitlementStatus(appIds, carrierConfig, request); verify(mMockHttpClient).request(mHttpRequestCaptor.capture()); - assertThat(mHttpRequestCaptor.getAllValues().get(0).url()) - .contains(ServiceEntitlement.APP_VOWIFI); - assertThat(mHttpRequestCaptor.getAllValues().get(0).url()) - .contains(ServiceEntitlement.APP_VOLTE); + assertThat(mHttpRequestCaptor.getValue().url()).contains(ServiceEntitlement.APP_VOWIFI); + assertThat(mHttpRequestCaptor.getValue().url()).contains(ServiceEntitlement.APP_VOLTE); + assertThat(mHttpRequestCaptor.getValue().timeoutInSec()).isEqualTo(70); + assertThat(mHttpRequestCaptor.getValue().network()).isNull(); } @Test - public void queryEntitlementStatus_noAuthenticationTokenContentTypeNotJson_throwException() + public void queryEntitlementStatus_noAuthenticationToken_contentTypeNotJson_throwException() throws Exception { HttpResponse xmlResponse = HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML) .build(); when(mMockHttpClient.request(any())).thenReturn(xmlResponse); + CarrierConfig carrierConfig = + CarrierConfig.builder().setServerUrl(TEST_URL).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); - try { - mEapAkaApi.queryEntitlementStatus(ImmutableList.of(ServiceEntitlement.APP_VOWIFI), - TEST_URL, - request); - fail(); - } catch (ServiceEntitlementException exception) { - assertThat(exception.getErrorCode()).isEqualTo( - ServiceEntitlementException.MALFORMED_HTTP_RESPONSE); - assertThat(exception.getMessage()).isEqualTo("Unexpected http ContentType"); - } + ServiceEntitlementException exception = expectThrows( + ServiceEntitlementException.class, + () -> mEapAkaApi.queryEntitlementStatus( + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), + carrierConfig, + request)); + + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.MALFORMED_HTTP_RESPONSE); + assertThat(exception.getMessage()).isEqualTo("Unexpected http ContentType"); } @Test - public void queryEntitlementStatus_noAuthenticationTokenEmptyResponseBody_throwException() + public void queryEntitlementStatus_noAuthenticationToken_emptyResponseBody_throwException() throws Exception { HttpResponse eapChallengeResponse = HttpResponse.builder().setContentType(ContentType.JSON).build(); when(mMockHttpClient.request(any())).thenReturn(eapChallengeResponse); + CarrierConfig carrierConfig = + CarrierConfig.builder().setServerUrl(TEST_URL).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); - try { - mEapAkaApi.queryEntitlementStatus(ImmutableList.of(ServiceEntitlement.APP_VOWIFI), - TEST_URL, - request); - fail(); - } catch (ServiceEntitlementException exception) { - assertThat(exception.getErrorCode()).isEqualTo( - ServiceEntitlementException.MALFORMED_HTTP_RESPONSE); - assertThat(exception.getMessage()).isEqualTo("Failed to parse json object"); - assertThat(exception.getCause()).isInstanceOf(JSONException.class); - } + ServiceEntitlementException exception = expectThrows( + ServiceEntitlementException.class, + () -> mEapAkaApi.queryEntitlementStatus( + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), + carrierConfig, + request)); + + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.MALFORMED_HTTP_RESPONSE); + assertThat(exception.getMessage()).isEqualTo("Failed to parse json object"); + assertThat(exception.getCause()).isInstanceOf(JSONException.class); } } diff --git a/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java index 3be93ca..6a08295 100644 --- a/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java +++ b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java @@ -18,20 +18,27 @@ package com.android.libraries.entitlement.http; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.expectThrows; import static java.nio.charset.StandardCharsets.UTF_8; +import android.net.Network; + import androidx.test.runner.AndroidJUnit4; import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.http.HttpConstants.ContentType; import com.android.libraries.entitlement.http.HttpConstants.RequestMethod; import com.android.libraries.entitlement.testing.FakeURLStreamHandler; +import com.android.libraries.entitlement.testing.FakeURLStreamHandler.FakeHttpsURLConnection; import com.android.libraries.entitlement.testing.FakeURLStreamHandler.FakeResponse; import com.google.common.collect.ImmutableMap; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,7 +56,7 @@ public class HttpClientTest { private static FakeURLStreamHandler sFakeURLStreamHandler; - private final HttpClient mHttpClient = new HttpClient(); + private HttpClient mHttpClient; @BeforeClass public static void setupURLStreamHandlerFactory() { @@ -57,6 +64,14 @@ public class HttpClientTest { URL.setURLStreamHandlerFactory(sFakeURLStreamHandler); } + @Before + public void setUp() { + // Reset sFakeURLStreamHandler + sFakeURLStreamHandler.stubResponse(ImmutableMap.of()); + + mHttpClient = new HttpClient(); + } + @Test public void request_contentTypeXml_returnsXmlBody() throws Exception { FakeResponse responseContent = @@ -66,13 +81,23 @@ public class HttpClientTest { .setResponseBody(TEST_RESPONSE_BODY.getBytes(UTF_8)) .setContentType(CONTENT_TYPE_STRING_JSON) .build(); - HttpRequest request = - HttpRequest.builder().setUrl(TEST_URL).setRequestMethod(RequestMethod.GET).build(); Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent); sFakeURLStreamHandler.stubResponse(response); + HttpRequest request = + HttpRequest.builder() + .setUrl(TEST_URL) + .setRequestMethod(RequestMethod.GET) + .setTimeoutInSec(70) + .build(); HttpResponse httpResponse = mHttpClient.request(request); + // Verify that one HttpURLConnection was opened and its timeout is 70 seconds. + assertThat(sFakeURLStreamHandler.getConnections()).hasSize(1); + HttpURLConnection connection = sFakeURLStreamHandler.getConnections().get(0); + assertThat(connection.getConnectTimeout()).isEqualTo(70 * 1000); + assertThat(connection.getReadTimeout()).isEqualTo(70 * 1000); + // Verify the HttpResponse. assertThat(httpResponse.contentType()).isEqualTo(ContentType.JSON); assertThat(httpResponse.body()).isEqualTo(TEST_RESPONSE_BODY); assertThat(httpResponse.responseCode()).isEqualTo(HttpURLConnection.HTTP_OK); @@ -93,15 +118,53 @@ public class HttpClientTest { Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent); sFakeURLStreamHandler.stubResponse(response); - try { - mHttpClient.request(request); - fail(); - } catch (ServiceEntitlementException exception) { - assertThat(exception.getErrorCode()).isEqualTo( - ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS); - assertThat(exception.getHttpStatus()).isEqualTo(HttpURLConnection.HTTP_BAD_REQUEST); - assertThat(exception.getMessage()).isEqualTo("Invalid connection response"); - assertThat(exception.getRetryAfter()).isEqualTo(RETRY_AFTER); - } + ServiceEntitlementException exception = + expectThrows(ServiceEntitlementException.class, () -> mHttpClient.request(request)); + + // Verify the ServiceEntitlementException. + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS); + assertThat(exception.getHttpStatus()).isEqualTo(HttpURLConnection.HTTP_BAD_REQUEST); + assertThat(exception).hasMessageThat().contains("Invalid connection response"); + assertThat(exception.getRetryAfter()).isEqualTo(RETRY_AFTER); + // Verify that one HttpURLConnection was opened and its timeout is 30 seconds. + assertThat(sFakeURLStreamHandler.getConnections()).hasSize(1); + HttpURLConnection connection = sFakeURLStreamHandler.getConnections().get(0); + assertThat(connection.getConnectTimeout()).isEqualTo(30 * 1000); + assertThat(connection.getReadTimeout()).isEqualTo(30 * 1000); + } + + @Test + public void request_contentTypeXml_returnsXmlBody_useSpecificNetwork() throws Exception { + FakeResponse responseContent = + FakeResponse.builder() + .setResponseCode(HttpURLConnection.HTTP_OK) + .setResponseLocation(null) + .setResponseBody(TEST_RESPONSE_BODY.getBytes(UTF_8)) + .setContentType(CONTENT_TYPE_STRING_JSON) + .build(); + Network network = mock(Network.class); + URL url = new URL(TEST_URL); + FakeHttpsURLConnection connection = new FakeHttpsURLConnection(url, responseContent); + when(network.openConnection(url)).thenReturn(connection); + HttpRequest request = + HttpRequest.builder() + .setUrl(TEST_URL) + .setRequestMethod(RequestMethod.GET) + .setNetwork(network) + .setTimeoutInSec(70) + .build(); + + HttpResponse httpResponse = mHttpClient.request(request); + + // Verify that the HttpURLConnection associsted with Netwotk was opened + // and its timeout is 70 seconds. + verify(network).openConnection(url); + assertThat(connection.getConnectTimeout()).isEqualTo(70 * 1000); + assertThat(connection.getReadTimeout()).isEqualTo(70 * 1000); + // Verify the HttpResponse. + assertThat(httpResponse.contentType()).isEqualTo(ContentType.JSON); + assertThat(httpResponse.body()).isEqualTo(TEST_RESPONSE_BODY); + assertThat(httpResponse.responseCode()).isEqualTo(HttpURLConnection.HTTP_OK); } -}
\ No newline at end of file +} diff --git a/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java b/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java index d323412..bee3d0c 100644 --- a/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java +++ b/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java @@ -17,6 +17,7 @@ package com.android.libraries.entitlement.testing; import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.ByteArrayInputStream; @@ -37,12 +38,12 @@ import javax.annotation.Nullable; import javax.net.ssl.HttpsURLConnection; /** - * Fakes {@linkplain java.net.URLStreamHandler} which is used to set URLStreamHandlerFactory for URL - * as {@linkplain java.net.URL} is a final class and cannot be mocked using mockito. + * A {@link URLStreamHandlerFactory} to return faked {@link URLConnection}, as {@link URL} is a + * final class and {@link URL#openConnection} cannot be mocked using mockito. */ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamHandlerFactory { - - private Map<String, FakeResponse> mResponseMap; + private Map<String, FakeResponse> mResponses = ImmutableMap.of(); + private List<FakeHttpsURLConnection> mConnections = new ArrayList<>(); private static final String ACCESS_TOKEN = "8dGozfI6%2FEaSsE7LaTfJKwdy"; private static final String LOCATION = "Location"; @@ -51,7 +52,9 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH @Override public URLConnection openConnection(URL u) { - FakeHttpsURLConnection connection = new FakeHttpsURLConnection(u); + FakeHttpsURLConnection connection = + new FakeHttpsURLConnection(u, mResponses.get(u.toString())); + mConnections.add(connection); return connection; } @@ -60,50 +63,48 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH return this; } + /** + * Prepares canned responses. Must be called before using this handler to open any {@link + * URLConnection}. + */ public FakeURLStreamHandler stubResponse(Map<String, FakeResponse> response) { - this.mResponseMap = response; + mResponses = response; + mConnections = new ArrayList<>(); return this; } - /** - * Fakes {@linkplain java.net.HttpURLConnection} to avoid making any network connection. - */ - public class FakeHttpsURLConnection extends HttpsURLConnection { - - public ByteArrayOutputStream mByteArrayOutputStream; + /** Returns {@link URLConnection}s opened by this handler since last {@link #stubResponse}. */ + public ImmutableList<FakeHttpsURLConnection> getConnections() { + return ImmutableList.copyOf(mConnections); + } - private final String mUrlString; + /** Faked {@link HttpsURLConnection} to avoid making any network connection. */ + public static class FakeHttpsURLConnection extends HttpsURLConnection { + private final FakeResponse mResponse; - protected FakeHttpsURLConnection(URL url) { + public FakeHttpsURLConnection(URL url, FakeResponse response) { super(url); - this.mUrlString = url.toString(); + mResponse = response; } @Override public InputStream getInputStream() throws IOException { - InputStream inputStream = new ByteArrayInputStream( - mResponseMap.get(mUrlString).responseBody()); - if (inputStream == null) { - throw new IOException(); - } - return inputStream; + return new ByteArrayInputStream(mResponse.responseBody()); } @Override public OutputStream getOutputStream() { - mByteArrayOutputStream = new ByteArrayOutputStream(); - return mByteArrayOutputStream; + return new ByteArrayOutputStream(); } @Override public int getResponseCode() { - return mResponseMap.get(mUrlString).responseCode(); + return mResponse.responseCode(); } @Override public Map<String, List<String>> getHeaderFields() { - List<String> locationList = new ArrayList<>(); - locationList.add("access_token=" + ACCESS_TOKEN); + List<String> locationList = ImmutableList.of("access_token=" + ACCESS_TOKEN); return ImmutableMap.of("Location", locationList); } @@ -111,11 +112,11 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH public String getHeaderField(String name) { switch (name) { case LOCATION: - return "Location: " + mResponseMap.get(mUrlString).responseLocation(); + return "Location: " + mResponse.responseLocation(); case CONTENT_TYPE: - return mResponseMap.get(mUrlString).contentType(); + return mResponse.contentType(); case RETRY_AFTER: - return mResponseMap.get(mUrlString).retryAfter(); + return mResponse.retryAfter(); default: return null; } @@ -188,4 +189,4 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH public abstract FakeResponse build(); } } -}
\ No newline at end of file +} |