summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-06-26 23:07:46 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-06-26 23:07:46 +0000
commitb99825c96b107df914cf8a7deb3a53bcb468d214 (patch)
treeb4c71e2ab9f8c556f9374f27b15d6d5ca05f0f12
parent77147a649d2a600c855c873157ae2f7c9a19b1a3 (diff)
parent0339abb301041048911ee8bcadf0f12bad3d237a (diff)
downloadImsServiceEntitlement-b99825c96b107df914cf8a7deb3a53bcb468d214.tar.gz
Snap for 7497336 from 0339abb301041048911ee8bcadf0f12bad3d237a to sc-d2-release
Change-Id: I8d8d8910d90a79b983459e4028c3716ae9e057f6
-rw-r--r--Android.bp8
-rw-r--r--src/com/android/imsserviceentitlement/ImsEntitlementApi.java69
-rw-r--r--src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java30
-rw-r--r--src/com/android/imsserviceentitlement/entitlement/EntitlementResult.java7
-rw-r--r--tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java64
-rw-r--r--tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java38
6 files changed, 200 insertions, 16 deletions
diff --git a/Android.bp b/Android.bp
index 8af066a..60ba277 100644
--- a/Android.bp
+++ b/Android.bp
@@ -44,6 +44,14 @@ android_library {
"play-services-cloud-messaging-aar",
"play-services-tasks-aar",
"transport-api-aar",
+ "firebase-measurement-connector-aar",
+ "firebase-encoders-json-aar",
+ "firebase-datatransport-aar",
+ "play-services-stats-aar",
+ "transport-runtime-aar",
+ "transport-backend-cct-aar",
+ "jsr330",
+ "dagger2",
],
libs: [
"auto_value_annotations",
diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
index 98d9de6..7a906fd 100644
--- a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
+++ b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
@@ -16,7 +16,11 @@
package com.android.imsserviceentitlement;
+import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
+import static java.time.temporal.ChronoUnit.SECONDS;
+
import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -40,12 +44,19 @@ import com.android.libraries.entitlement.ServiceEntitlementException;
import com.android.libraries.entitlement.ServiceEntitlementRequest;
import com.google.common.collect.ImmutableList;
+import com.google.common.net.HttpHeaders;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
/** Implementation of the entitlement API. */
public class ImsEntitlementApi {
private static final String TAG = "IMSSE-ImsEntitlementApi";
+ private static final int RESPONSE_RETRY_AFTER = 503;
private static final int RESPONSE_TOKEN_EXPIRED = 511;
+
private static final int AUTHENTICATION_RETRIES = 1;
private final Context mContext;
@@ -56,6 +67,9 @@ public class ImsEntitlementApi {
private int mRetryFullAuthenticationCount = AUTHENTICATION_RETRIES;
private boolean mNeedsImsProvisioning;
+ @VisibleForTesting
+ static Clock sClock = Clock.systemUTC();
+
public ImsEntitlementApi(Context context, int subId) {
this.mContext = context;
this.mSubId = subId;
@@ -81,8 +95,8 @@ public class ImsEntitlementApi {
/**
* Returns WFC entitlement check result from carrier API (over network), or {@code null} on
- * unrecoverable network issue or malformed server response. This is blocking call so should not
- * be called on main thread.
+ * unrecoverable network issue or malformed server response. This is blocking call so should
+ * not be called on main thread.
*/
@Nullable
public EntitlementResult checkEntitlementStatus() {
@@ -119,24 +133,53 @@ public class ImsEntitlementApi {
// Reset the retry count if no exception from queryEntitlementStatus()
mRetryFullAuthenticationCount = AUTHENTICATION_RETRIES;
} catch (ServiceEntitlementException e) {
- if (e.getErrorCode()
- == ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS
- && e.getHttpStatus() == RESPONSE_TOKEN_EXPIRED) {
- if (mRetryFullAuthenticationCount <= 0) {
- Log.d(TAG, "Ran out of the retry count, stop query status.");
- return null;
+ if (e.getErrorCode() == ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS) {
+ if (e.getHttpStatus() == RESPONSE_TOKEN_EXPIRED) {
+ if (mRetryFullAuthenticationCount <= 0) {
+ Log.d(TAG, "Ran out of the retry count, stop query status.");
+ return null;
+ }
+ Log.d(TAG, "Server asking for full authentication, retry the query.");
+ // Clean up the cached data and perform full authentication next query.
+ mLastEntitlementConfiguration.reset();
+ mRetryFullAuthenticationCount--;
+ return checkEntitlementStatus();
+ } else if (e.getHttpStatus() == RESPONSE_RETRY_AFTER && !TextUtils.isEmpty(
+ e.getRetryAfter())) {
+ // For handling the case of HTTP_UNAVAILABLE(503), client would perform the
+ // retry for the delay of Retry-After.
+ Log.d(TAG, "Server asking for retry. retryAfter = " + e.getRetryAfter());
+ return EntitlementResult
+ .builder()
+ .setRetryAfterSeconds(parseDelaySecondsByRetryAfter(e.getRetryAfter()))
+ .build();
}
- Log.d(TAG, "Server asking for full authentication, retry the query.");
- // Clean up the cached data and perform full authentication next query.
- mLastEntitlementConfiguration.reset();
- mRetryFullAuthenticationCount--;
- return checkEntitlementStatus();
}
Log.e(TAG, "queryEntitlementStatus failed", e);
}
return entitlementXmlDoc == null ? null : toEntitlementResult(entitlementXmlDoc);
}
+ /**
+ * Parses the value of {@link HttpHeaders#RETRY_AFTER}. The possible formats could be a numeric
+ * value in second, or a HTTP-date in RFC-1123 date-time format.
+ */
+ private long parseDelaySecondsByRetryAfter(String retryAfter) {
+ try {
+ return Long.parseLong(retryAfter);
+ } catch (NumberFormatException numberFormatException) {
+ }
+
+ try {
+ return SECONDS.between(
+ Instant.now(sClock), RFC_1123_DATE_TIME.parse(retryAfter, Instant::from));
+ } catch (DateTimeParseException dateTimeParseException) {
+ }
+
+ Log.w(TAG, "Unable to parse retry-after: " + retryAfter + ", ignore it.");
+ return -1;
+ }
+
private EntitlementResult toEntitlementResult(XmlDoc doc) {
EntitlementResult.Builder builder = EntitlementResult.builder();
ClientBehavior clientBehavior = mLastEntitlementConfiguration.entitlementValidation();
diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
index 3cb748b..bd9ab76 100644
--- a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
+++ b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
@@ -71,7 +71,8 @@ public class ImsEntitlementPollingService extends JobService {
*/
private final SparseArray<EntitlementPollingTask> mTasks = new SparseArray<>();
- @VisibleForTesting EntitlementPollingTask mOngoingTask;
+ @VisibleForTesting
+ EntitlementPollingTask mOngoingTask;
@Override
@VisibleForTesting
@@ -200,7 +201,6 @@ public class ImsEntitlementPollingService extends JobService {
if (mNeedsImsProvisioning) {
// TODO(b/190476343): Unify EntitlementResult and EntitlementConfiguration.
doImsEntitlementCheck();
- checkVersValidity();
} else {
doWfcEntitlementCheck();
}
@@ -212,6 +212,10 @@ public class ImsEntitlementPollingService extends JobService {
EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
Log.d(TAG, "Entitlement result: " + result);
+ if (performRetryIfNeeded(result)) {
+ return;
+ }
+
if (shouldTurnOffWfc(result)) {
mImsUtils.setVowifiProvisioned(false);
mVowifiResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED;
@@ -241,6 +245,7 @@ public class ImsEntitlementPollingService extends JobService {
mSmsoipResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__FAILED;
Log.d(TAG, "checkEntitlementStatus failed.", e);
}
+ checkVersValidity();
}
@WorkerThread
@@ -252,6 +257,11 @@ public class ImsEntitlementPollingService extends JobService {
try {
EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
Log.d(TAG, "Entitlement result: " + result);
+
+ if (performRetryIfNeeded(result)) {
+ return;
+ }
+
if (shouldTurnOffWfc(result)) {
mVowifiResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED;
mImsUtils.disableWfc();
@@ -265,6 +275,22 @@ public class ImsEntitlementPollingService extends JobService {
}
/**
+ * Performs retry if needed. Returns true if {@link ImsEntitlementPollingService} has
+ * scheduled.
+ */
+ private boolean performRetryIfNeeded(@Nullable EntitlementResult result) {
+ if (result == null || result.getRetryAfterSeconds() < 0) {
+ return false;
+ }
+ mVowifiResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__FAILED;
+ ImsEntitlementPollingService.enqueueJobWithDelay(
+ ImsEntitlementPollingService.this,
+ mSubid,
+ result.getRetryAfterSeconds());
+ return true;
+ }
+
+ /**
* Schedules entitlement status check after a VERS.validity time, if the last valid is
* during validity.
*/
diff --git a/src/com/android/imsserviceentitlement/entitlement/EntitlementResult.java b/src/com/android/imsserviceentitlement/entitlement/EntitlementResult.java
index 4bc430a..480d78a 100644
--- a/src/com/android/imsserviceentitlement/entitlement/EntitlementResult.java
+++ b/src/com/android/imsserviceentitlement/entitlement/EntitlementResult.java
@@ -51,7 +51,8 @@ public abstract class EntitlementResult {
.setSmsoveripStatus(INACTIVE_SMSOVERIP_STATUS)
.setEmergencyAddressWebUrl("")
.setEmergencyAddressWebData("")
- .setTermsAndConditionsWebUrl("");
+ .setTermsAndConditionsWebUrl("")
+ .setRetryAfterSeconds(-1);
}
/** The entitlement and service status of VoWiFi. */
@@ -66,6 +67,8 @@ public abstract class EntitlementResult {
public abstract String getEmergencyAddressWebData();
/** The URL to the WFC T&C web form. */
public abstract String getTermsAndConditionsWebUrl();
+ /** Service temporary unavailable, retry the status check after a delay in seconds. */
+ public abstract long getRetryAfterSeconds();
/** Builder of {@link EntitlementResult}. */
@AutoValue.Builder
@@ -77,6 +80,7 @@ public abstract class EntitlementResult {
public abstract Builder setEmergencyAddressWebUrl(String emergencyAddressWebUrl);
public abstract Builder setEmergencyAddressWebData(String emergencyAddressWebData);
public abstract Builder setTermsAndConditionsWebUrl(String termsAndConditionsWebUrl);
+ public abstract Builder setRetryAfterSeconds(long retryAfter);
}
@Override
@@ -88,6 +92,7 @@ public abstract class EntitlementResult {
builder.append(",getEmergencyAddressWebUrl=").append(opaque(getEmergencyAddressWebUrl()));
builder.append(",getEmergencyAddressWebData=").append(opaque(getEmergencyAddressWebData()));
builder.append(",getTermsAndConditionsWebUrl=").append(getTermsAndConditionsWebUrl());
+ builder.append(",getRetryAfter=").append(getRetryAfterSeconds());
builder.append("}");
return builder.toString();
}
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
index 991f76d..d0ea3ee 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
@@ -53,6 +53,14 @@ import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.text.SimpleDateFormat;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
@RunWith(AndroidJUnit4.class)
public class ImsEntitlementApiTest {
@Rule public final MockitoRule rule = MockitoJUnit.rule();
@@ -239,6 +247,55 @@ public class ImsEntitlementApiTest {
assertThat(mEntitlementConfiguration.getToken().get()).isEqualTo("NEW_TOKEN");
}
+ @Test
+ public void checkEntitlementStatus_httpResponse503WithDateTime_returnsRetryAfter()
+ throws Exception {
+ setImsProvisioningBool(false);
+ setupImsEntitlementApi(mEntitlementConfiguration);
+ mEntitlementConfiguration.update(RAW_XML);
+ Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(0), ZoneOffset.UTC);
+ ImsEntitlementApi.sClock = fixedClock;
+
+ // While perform fast-authn, throws exception with code 503
+ when(mMockServiceEntitlement.queryEntitlementStatus(
+ ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
+ authenticationRequest("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX")))
+ .thenThrow(
+ new ServiceEntitlementException(
+ ERROR_HTTP_STATUS_NOT_SUCCESS,
+ 503,
+ getDateTimeAfter(120, fixedClock),
+ "Invalid connection response"));
+
+ EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
+
+ assertThat(result).isNotNull();
+ assertThat(result.getRetryAfterSeconds()).isEqualTo(120);
+ }
+
+ @Test
+ public void checkEntitlementStatus_httpResponse503WithNumericValue_returnsRetryAfter()
+ throws Exception {
+ setImsProvisioningBool(false);
+ setupImsEntitlementApi(mEntitlementConfiguration);
+ mEntitlementConfiguration.update(RAW_XML);
+ // While perform fast-authn, throws exception with code 503
+ when(mMockServiceEntitlement.queryEntitlementStatus(
+ ImmutableList.of(ServiceEntitlement.APP_VOWIFI),
+ authenticationRequest("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX")))
+ .thenThrow(
+ new ServiceEntitlementException(
+ ERROR_HTTP_STATUS_NOT_SUCCESS,
+ 503,
+ "120",
+ "Invalid connection response"));
+
+ EntitlementResult result = mImsEntitlementApi.checkEntitlementStatus();
+
+ assertThat(result).isNotNull();
+ assertThat(result.getRetryAfterSeconds()).isEqualTo(120);
+ }
+
private ServiceEntitlementRequest authenticationRequest(String token) {
ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
if (token != null) {
@@ -271,4 +328,11 @@ public class ImsEntitlementApiTest {
when(mContext.getSystemService(CarrierConfigManager.class))
.thenReturn(mCarrierConfigManager);
}
+
+ private String getDateTimeAfter(long seconds, Clock fixedClock) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(
+ "EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ return dateFormat.format(Date.from(fixedClock.instant().plusSeconds(seconds)));
+ }
}
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java
index 2737c62..ce71f2c 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementPollingServiceTest.java
@@ -18,6 +18,7 @@ package com.android.imsserviceentitlement;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -160,6 +161,43 @@ public class ImsEntitlementPollingServiceTest {
}
@Test
+ public void doEntitlementCheck_ImsEntitlementShouldRetry_rescheduleJob() throws Exception {
+ setImsProvisioningBool(true);
+ EntitlementResult entitlementResult =
+ EntitlementResult.builder().setRetryAfterSeconds(120).build();
+ when(mImsEntitlementApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+
+ mService.onStartJob(mJobParameters);
+ mService.mOngoingTask.get(); // wait for job finish.
+
+ verify(mImsUtils, never()).setVolteProvisioned(anyBoolean());
+ verify(mImsUtils, never()).setVowifiProvisioned(anyBoolean());
+ verify(mImsUtils, never()).setSmsoipProvisioned(anyBoolean());
+ assertThat(
+ mScheduler.getPendingJob(
+ jobIdWithSubId(JobManager.QUERY_ENTITLEMENT_STATUS_JOB_ID, SUB_ID)))
+ .isNotNull();
+ }
+
+ @Test
+ public void doEntitlementCheck_WfcEntitlementShouldRetry_rescheduleJob() throws Exception {
+ EntitlementResult entitlementResult =
+ EntitlementResult.builder().setRetryAfterSeconds(120).build();
+ when(mImsEntitlementApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+
+ mService.onStartJob(mJobParameters);
+ mService.mOngoingTask.get(); // wait for job finish.
+
+ verify(mImsUtils, never()).setVolteProvisioned(anyBoolean());
+ verify(mImsUtils, never()).setVowifiProvisioned(anyBoolean());
+ verify(mImsUtils, never()).setSmsoipProvisioned(anyBoolean());
+ assertThat(
+ mScheduler.getPendingJob(
+ jobIdWithSubId(JobManager.QUERY_ENTITLEMENT_STATUS_JOB_ID, SUB_ID)))
+ .isNotNull();
+ }
+
+ @Test
public void enqueueJob_hasJob() {
ImsEntitlementPollingService.enqueueJob(mContext, SUB_ID, 0);