diff options
8 files changed, 285 insertions, 8 deletions
diff --git a/coverage.sh b/coverage.sh new file mode 100755 index 0000000..7cd2bf2 --- /dev/null +++ b/coverage.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +##### App specific parameters ##### + +PACKAGE_NAME='com.android.libraries.entitlement' +MODULE_NAME='service-entitlement' +MODULE_PATH='frameworks/libs/service_entitlement' + +TEST_PACKAGE='com.android.libraries.entitlement.tests' +TEST_MODULE_NAME='service-entitlement-tests' +TEST_MODULE_PATH='frameworks/libs/service_entitlement/tests' +TEST_MODULE_INSTALL_PATH="testcases/$TEST_MODULE_NAME/arm64/$TEST_MODULE_NAME.apk" +TEST_RUNNER="$TEST_PACKAGE/androidx.test.runner.AndroidJUnitRunner" + +##### End app specific parameters ##### + +if [[ $# != 0 && ! ($# == 1 && ($1 == "html" || $1 == "xml" || $1 == "csv")) ]]; then + echo "$0: usage: coverage.sh [REPORT_TYPE]" + echo "REPORT_TYPE [html | xml | csv] : the type of the report (default is html)" + exit 1 +fi + +REPORT_TYPE=${1:-html} + +if [ -z $ANDROID_BUILD_TOP ]; then + echo "You need to source and lunch before you can use this script" + exit 1 +fi + +REPORTER_JAR="$ANDROID_BUILD_TOP/out/soong/host/linux-x86/framework/jacoco-cli.jar" + +OUTPUT_DIR="$ANDROID_BUILD_TOP/out/coverage/$MODULE_NAME" + +echo "Running tests and generating coverage report" +echo "Output dir: $OUTPUT_DIR" +echo "Report type: $REPORT_TYPE" + +# location on the device to store coverage results, need to be accessible by the app +REMOTE_COVERAGE_OUTPUT_FILE="/data/user/0/$TEST_PACKAGE/files/coverage.ec" + +COVERAGE_OUTPUT_FILE="$ANDROID_BUILD_TOP/out/$PACKAGE_NAME.ec" +OUT_COMMON="$ANDROID_BUILD_TOP/out/target/common" +COVERAGE_CLASS_FILE="$OUT/obj/JAVA_LIBRARIES/${MODULE_NAME}_intermediates/javalib.jar" + +source $ANDROID_BUILD_TOP/build/envsetup.sh + +set -e # fail early + +echo "" +echo "BUILDING PACKAGE $PACKAGE_NAME" +echo "============================================" +(cd "$ANDROID_BUILD_TOP/$MODULE_PATH" && EMMA_INSTRUMENT=true EMMA_INSTRUMENT_STATIC=true mma -j32) +echo "============================================" + +echo "" +echo "BUILDING TEST PACKAGE $TEST_MODULE_NAME" +echo "============================================" +(cd "$ANDROID_BUILD_TOP/$TEST_MODULE_PATH" && EMMA_INSTRUMENT=true EMMA_INSTRUMENT_STATIC=true mma -j32) +echo "============================================" + +#set -x # print commands + +adb root +adb wait-for-device + +adb shell rm -f "$REMOTE_COVERAGE_OUTPUT_FILE" + +adb install -r -g "$OUT/$TEST_MODULE_INSTALL_PATH" + +echo "" +echo "RUNNING TESTS $TEST_RUNNER" +echo "============================================" +adb shell am instrument -e coverage true -w $TEST_RUNNER +echo "============================================" + +mkdir -p "$OUTPUT_DIR" + +adb pull "$REMOTE_COVERAGE_OUTPUT_FILE" "$COVERAGE_OUTPUT_FILE" + +java -jar "$REPORTER_JAR" \ + report "$COVERAGE_OUTPUT_FILE" \ + --$REPORT_TYPE "$OUTPUT_DIR" \ + --classfiles "$COVERAGE_CLASS_FILE" \ + --sourcefiles "$ANDROID_BUILD_TOP/$MODULE_PATH/java" + +#set +x + +# Echo the file as URI to quickly open the result using ctrl-click in terminal +if [[ REPORT_TYPE == html ]] ; then + echo "COVERAGE RESULTS IN:" + echo "file://$OUTPUT_DIR/index.html" +fi diff --git a/java/com/android/libraries/entitlement/EapAkaHelper.java b/java/com/android/libraries/entitlement/EapAkaHelper.java index 3db48e1..e5af73e 100644 --- a/java/com/android/libraries/entitlement/EapAkaHelper.java +++ b/java/com/android/libraries/entitlement/EapAkaHelper.java @@ -20,6 +20,7 @@ import static com.android.libraries.entitlement.eapaka.EapAkaResponse.respondToE import android.content.Context; import android.telephony.TelephonyManager; +import android.util.Log; import androidx.annotation.Nullable; @@ -31,6 +32,8 @@ import com.android.libraries.entitlement.eapaka.EapAkaChallenge; * helpful to other apps. */ public class EapAkaHelper { + private static final String TAG = "ServiceEntitlement"; + private final Context mContext; private final int mSimSubscriptionId; @@ -103,6 +106,7 @@ public class EapAkaHelper { return new EapAkaResponse( eapAkaResponse.response(), eapAkaResponse.synchronizationFailureResponse()); } catch (ServiceEntitlementException e) { + Log.i(TAG, "Failed to generate EAP-AKA response", e); return null; } } diff --git a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java index e54d87f..7e2b0f1 100644 --- a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java +++ b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java @@ -18,8 +18,14 @@ package com.android.libraries.entitlement; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.testng.Assert.expectThrows; +import android.content.Context; +import android.telephony.TelephonyManager; + +import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; import com.android.libraries.entitlement.eapaka.EapAkaApi; @@ -38,13 +44,20 @@ import org.mockito.junit.MockitoRule; public class ServiceEntitlementTest { private static final String QUERY_APP_VOLTE_RESULT = "QUERY_APP_VOLTE_RESULT"; private static final String QUERY_APP_VOWIFI_RESULT = "QUERY_APP_VOWIFI_RESULT"; + private static final String QUERY_APP_ODSA_COMPANION_RESULT = "QUERY_APP_ODSA_COMPANION_RESULT"; + private static final String QUERY_APP_ODSA_PRIMARY_RESULT = "QUERY_APP_ODSA_PRIMARY_RESULT"; private static final String TEST_URL = "https://test.url"; - @Rule - public final MockitoRule rule = MockitoJUnit.rule(); - @Mock - EapAkaApi mMockEapAkaApi; + private static final String IMSI = "234107813240779"; + private static final String MCCMNC = "23410"; + private static final int SUB_ID = 1; + + @Rule public final MockitoRule rule = MockitoJUnit.rule(); + @Mock EapAkaApi mMockEapAkaApi; + @Mock private TelephonyManager mMockTelephonyManager; + @Mock private TelephonyManager mMockTelephonyManagerForSubId; + private Context mContext; private ServiceEntitlement mServiceEntitlement; private CarrierConfig mCarrierConfig; @@ -52,6 +65,31 @@ public class ServiceEntitlementTest { public void setUp() { mCarrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build(); mServiceEntitlement = new ServiceEntitlement(mCarrierConfig, mMockEapAkaApi); + mContext = spy(ApplicationProvider.getApplicationContext()); + } + + @Test + public void queryEntitlementStatus_noServerAddress_throwException() throws Exception { + CarrierConfig config = CarrierConfig.builder().build(); + ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); + ServiceEntitlement serviceEntitlement = new ServiceEntitlement(mContext, config, SUB_ID); + when(mContext.getSystemService(TelephonyManager.class)) + .thenReturn(mMockTelephonyManager); + when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)) + .thenReturn(mMockTelephonyManagerForSubId); + when(mMockTelephonyManagerForSubId.getSubscriberId()).thenReturn(IMSI); + when(mMockTelephonyManagerForSubId.getSimOperator()).thenReturn(MCCMNC); + + ServiceEntitlementException exception = expectThrows( + ServiceEntitlementException.class, + () -> serviceEntitlement.queryEntitlementStatus( + ImmutableList.of(ServiceEntitlement.APP_VOWIFI), request)); + + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.ERROR_SERVER_NOT_CONNECTABLE); + assertThat(exception.getMessage()).isEqualTo("Configure connection failed!"); + assertThat(exception.getHttpStatus()).isEqualTo(0); + assertThat(exception.getRetryAfter()).isEmpty(); } @Test @@ -79,4 +117,32 @@ public class ServiceEntitlementTest { request)) .isEqualTo(QUERY_APP_VOWIFI_RESULT); } + + @Test + public void performEsimOdsa_appOdsaCompanion_returnResult() throws Exception { + ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); + EsimOdsaOperation odsaOperation = EsimOdsaOperation.builder().build(); + when(mMockEapAkaApi.performEsimOdsaOperation( + ServiceEntitlement.APP_ODSA_COMPANION, mCarrierConfig, request, odsaOperation)) + .thenReturn(QUERY_APP_ODSA_COMPANION_RESULT); + + assertThat( + mServiceEntitlement.performEsimOdsa( + ServiceEntitlement.APP_ODSA_COMPANION, request, odsaOperation)) + .isEqualTo(QUERY_APP_ODSA_COMPANION_RESULT); + } + + @Test + public void performEsimOdsa_appOdsaPrimary_returnResult() throws Exception { + ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build(); + EsimOdsaOperation odsaOperation = EsimOdsaOperation.builder().build(); + when(mMockEapAkaApi.performEsimOdsaOperation( + ServiceEntitlement.APP_ODSA_PRIMARY, mCarrierConfig, request, odsaOperation)) + .thenReturn(QUERY_APP_ODSA_PRIMARY_RESULT); + + assertThat( + mServiceEntitlement.performEsimOdsa( + ServiceEntitlement.APP_ODSA_PRIMARY, request, odsaOperation)) + .isEqualTo(QUERY_APP_ODSA_PRIMARY_RESULT); + } } diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java index f655258..b837695 100644 --- a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java +++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java @@ -220,6 +220,8 @@ public class EapAkaApiTest { ServiceEntitlementException.ERROR_MALFORMED_HTTP_RESPONSE); assertThat(exception.getMessage()).isEqualTo("Failed to parse json object"); assertThat(exception.getCause()).isInstanceOf(JSONException.class); + assertThat(exception.getHttpStatus()).isEqualTo(0); + assertThat(exception.getRetryAfter()).isEmpty(); } @Test @@ -263,7 +265,7 @@ public class EapAkaApiTest { throws Exception { HttpResponse response = HttpResponse.builder().setBody(RESPONSE_XML).build(); when(mMockHttpClient.request(any())).thenReturn(response); - CarrierConfig carrierConfig = CarrierConfig.builder().build(); + CarrierConfig carrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest .builder() @@ -284,7 +286,7 @@ public class EapAkaApiTest { throws Exception { HttpResponse response = HttpResponse.builder().setBody(RESPONSE_XML).build(); when(mMockHttpClient.request(any())).thenReturn(response); - CarrierConfig carrierConfig = CarrierConfig.builder().build(); + CarrierConfig carrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build(); ServiceEntitlementRequest request = ServiceEntitlementRequest .builder() diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java index 219737d..e6d1c0c 100644 --- a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java +++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java @@ -104,6 +104,8 @@ public class EapAkaSecurityContextTest { .isEqualTo(ServiceEntitlementException.ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE); assertThat(exception.getMessage()) .isEqualTo("Invalid SIM EAP-AKA authentication response!"); + assertThat(exception.getHttpStatus()).isEqualTo(0); + assertThat(exception.getRetryAfter()).isEmpty(); } @Test diff --git a/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java index 5838bb7..505e8b5 100644 --- a/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java +++ b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java @@ -192,4 +192,56 @@ public class HttpClientTest { FakeHttpsURLConnection connection = sFakeURLStreamHandler.getConnections().get(0); assertThat(connection.getBytesWrittenToOutputStream()).isEqualTo(postData.getBytes(UTF_8)); } + + @Test + public void request_getResponseCodeFailed_expectThrowsException() { + HttpRequest request = + HttpRequest.builder() + .setUrl(TEST_URL) + .setRequestMethod(RequestMethod.GET) + .build(); + FakeResponse responseContent = + FakeResponse.builder() + .setResponseBody(TEST_RESPONSE_BODY.getBytes(UTF_8)) + .setContentType(CONTENT_TYPE_STRING_JSON) + .setHasException(true) + .build(); + Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent); + sFakeURLStreamHandler.stubResponse(response); + + ServiceEntitlementException exception = expectThrows( + ServiceEntitlementException.class, () -> mHttpClient.request(request)); + + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS); + assertThat(exception.getMessage()).isEqualTo("Read response code failed!"); + assertThat(exception.getHttpStatus()).isEqualTo(0); + assertThat(exception.getRetryAfter()).isEmpty(); + } + + @Test + public void request_getResponseBodyFailed_expectThrowsException() { + HttpRequest request = + HttpRequest.builder() + .setUrl(TEST_URL) + .setRequestMethod(RequestMethod.GET) + .build(); + FakeResponse responseContent = + FakeResponse.builder() + .setResponseCode(HttpURLConnection.HTTP_OK) + .setContentType(CONTENT_TYPE_STRING_JSON) + .setHasException(true) + .build(); + Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent); + sFakeURLStreamHandler.stubResponse(response); + + ServiceEntitlementException exception = expectThrows( + ServiceEntitlementException.class, () -> mHttpClient.request(request)); + + assertThat(exception.getErrorCode()).isEqualTo( + ServiceEntitlementException.ERROR_MALFORMED_HTTP_RESPONSE); + assertThat(exception.getMessage()).isEqualTo("Read response body/message failed!"); + assertThat(exception.getHttpStatus()).isEqualTo(0); + assertThat(exception.getRetryAfter()).isEmpty(); + } } diff --git a/tests/src/com/android/libraries/entitlement/utils/BytesConverterTest.java b/tests/src/com/android/libraries/entitlement/utils/BytesConverterTest.java new file mode 100644 index 0000000..41c8445 --- /dev/null +++ b/tests/src/com/android/libraries/entitlement/utils/BytesConverterTest.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.utils; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class BytesConverterTest { + @Test + public void convertBytesToHexString_nullBytes_returnsNull() { + assertThat(BytesConverter.convertBytesToHexString(null)).isNull(); + } + + @Test + public void convertBytesToHexString_integerBytes_returnsHexString() { + byte[] integerBytes = BytesConverter.convertIntegerTo4Bytes(123); + + String hexString = BytesConverter.convertBytesToHexString(integerBytes); + + assertThat(hexString).isEqualTo("0000007B"); + } +} diff --git a/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java b/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java index a5bdb4c..9f1d233 100644 --- a/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java +++ b/tests/utils/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java @@ -16,6 +16,8 @@ package com.android.libraries.entitlement.testing; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -90,6 +92,9 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH @Override public InputStream getInputStream() throws IOException { + if (mResponse.hasException() && mResponse.responseBody().length == 0) { + throw new IOException("stub exception"); + } return new ByteArrayInputStream(mResponse.responseBody()); } @@ -98,12 +103,20 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH return mOutputStream; } + @Override + public InputStream getErrorStream() { + return new ByteArrayInputStream("stub error".getBytes(UTF_8)); + } + public byte[] getBytesWrittenToOutputStream() { return mOutputStream.toByteArray(); } @Override - public int getResponseCode() { + public int getResponseCode() throws IOException { + if (mResponse.hasException() && mResponse.responseCode() == 0) { + throw new IOException("stub exception"); + } return mResponse.responseCode(); } @@ -170,13 +183,16 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH public abstract String retryAfter(); + abstract boolean hasException(); + public static Builder builder() { return new AutoValue_FakeURLStreamHandler_FakeResponse.Builder() .setResponseBody(new byte[]{}) .setContentType("") .setResponseCode(0) .setResponseLocation("") - .setRetryAfter(""); + .setRetryAfter("") + .setHasException(false); } @AutoValue.Builder @@ -191,6 +207,8 @@ public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamH public abstract Builder setRetryAfter(String retryAfter); + public abstract Builder setHasException(boolean hasException); + public abstract FakeResponse build(); } } |