Merge changes from topic "error_policy"
* changes:
Support initial attach for HO failures due to network issues
Update parser for error policy config
diff --git a/assets/defaultiwlanerrorconfig.json b/assets/defaultiwlanerrorconfig.json
index 7dd5818..212f611 100644
--- a/assets/defaultiwlanerrorconfig.json
+++ b/assets/defaultiwlanerrorconfig.json
@@ -50,6 +50,10 @@
# 3.4.3. APM_ENABLE_EVENT: APM off to on toggle.
# 3.4.4. WIFI_AP_CHANGED_EVENT: Wifi is connected to a AP with different SSID.
# 3.4.5. WIFI_CALLING_DISABLE_EVENT: Wifi calling button on to off toggle.
+# 3.5. "HandoverAttemptCount": Integer to specify the number of handover request attempts before
+# using initial attach instead. It is an optional field.
+# Note: This should only be defined in the config when handover attempt count is enabled and
+# "ErrorType" is explicitly defined as "IKE_PROTOCOL_ERROR_TYPE".
#
# Note: When the value is "*" for any of "ApnName" or "ErrorType" or "ErrorDetails",
# it means that the config definition applies to rest of the errors for which
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index b2eedbd..6d64814 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -32,6 +32,8 @@
import com.android.internal.annotations.VisibleForTesting;
+import com.google.auto.value.AutoValue;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -50,6 +52,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -444,6 +447,19 @@
return new IwlanError(IwlanError.NO_ERROR);
}
+ /**
+ * Returns whether framework should retry tunnel setup with initial PDN bringup request when
+ * handover request fails.
+ *
+ * @param apn apn name
+ * @return boolean result of whether framework should retry tunnel setup with initial PDN
+ * bringup request when handover request fails
+ */
+ public synchronized boolean shouldRetryWithInitialAttach(String apn) {
+ ErrorInfo errorInfo = mLastErrorForApn.get(apn);
+ return errorInfo != null && errorInfo.shouldRetryWithInitialAttach();
+ }
+
public void logErrorPolicies() {
Log.d(LOG_TAG, "mCarrierConfigPolicies:");
for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
@@ -560,7 +576,8 @@
return stringBuilder.toString();
}
- private Map<String, List<ErrorPolicy>> readErrorPolicies(JSONArray apnArray)
+ @VisibleForTesting
+ Map<String, List<ErrorPolicy>> readErrorPolicies(JSONArray apnArray)
throws JSONException, IllegalArgumentException {
Map<String, List<ErrorPolicy>> errorPolicies = new HashMap<>();
for (int i = 0; i < apnArray.length(); i++) {
@@ -580,14 +597,39 @@
throw new IllegalArgumentException("Unknown error type in the parsing");
}
- ErrorPolicy errorPolicy =
- new ErrorPolicy(
- errorType,
- parseErrorDetails(errorType, errorDetailArray),
- parseRetryArray((JSONArray) errorTypeObject.get("RetryArray")),
- errorTypeObject.optInt("NumAttemptsPerFqdn", -1),
- parseUnthrottlingEvents(
- (JSONArray) errorTypeObject.get("UnthrottlingEvents")));
+ List<Integer> retryArray =
+ parseRetryArray((JSONArray) errorTypeObject.get("RetryArray"));
+
+ ErrorPolicy.Builder errorPolicyBuilder =
+ ErrorPolicy.builder()
+ .setErrorType(errorType)
+ .setErrorDetails(parseErrorDetails(errorType, errorDetailArray))
+ .setRetryArray(retryArray)
+ .setUnthrottlingEvents(
+ parseUnthrottlingEvents(
+ (JSONArray)
+ errorTypeObject.get("UnthrottlingEvents")));
+
+ if (!retryArray.isEmpty() && retryArray.get(retryArray.size() - 1) == -1L) {
+ errorPolicyBuilder.setInfiniteRetriesWithLastRetryTime(true);
+ }
+
+ if (errorTypeObject.has("NumAttemptsPerFqdn")) {
+ errorPolicyBuilder.setNumAttemptsPerFqdn(
+ errorTypeObject.getInt("NumAttemptsPerFqdn"));
+ }
+
+ if (errorTypeObject.has("HandoverAttemptCount")) {
+ if (errorType != IKE_PROTOCOL_ERROR_TYPE) {
+ throw new IllegalArgumentException(
+ "Handover attempt count should not be applied when errorType is not"
+ + " explicitly defined as IKE_PROTOCOL_ERROR_TYPE");
+ }
+ errorPolicyBuilder.setHandoverAttemptCount(
+ errorTypeObject.getInt("HandoverAttemptCount"));
+ }
+
+ ErrorPolicy errorPolicy = errorPolicyBuilder.build();
errorPolicies.putIfAbsent(apnName, new ArrayList<ErrorPolicy>());
errorPolicies.get(apnName).add(errorPolicy);
@@ -727,13 +769,13 @@
for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
List<ErrorPolicy> errorPolicies = entry.getValue();
for (ErrorPolicy errorPolicy : errorPolicies) {
- events.addAll(errorPolicy.mUnthrottlingEvents);
+ events.addAll(errorPolicy.unthrottlingEvents());
}
}
for (Map.Entry<String, List<ErrorPolicy>> entry : mDefaultPolicies.entrySet()) {
List<ErrorPolicy> errorPolicies = entry.getValue();
for (ErrorPolicy errorPolicy : errorPolicies) {
- events.addAll(errorPolicy.mUnthrottlingEvents);
+ events.addAll(errorPolicy.unthrottlingEvents());
}
}
events.add(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT);
@@ -828,38 +870,60 @@
return mErrorStats;
}
- class ErrorPolicy {
- @ErrorPolicyErrorType int mErrorType;
- List<String> mErrorDetails;
- List<Integer> mRetryArray;
- int mNumAttemptsPerFqdn;
- List<Integer> mUnthrottlingEvents;
+ @AutoValue
+ abstract static class ErrorPolicy {
+ private static final String LOG_TAG = ErrorPolicyManager.class.getSimpleName();
- ErrorPolicy(
- @ErrorPolicyErrorType int errorType,
- List<String> errorDetails,
- List<Integer> retryArray,
- int numAttemptsPerFqdn,
- List<Integer> unthrottlingEvents) {
- mErrorType = errorType;
- mErrorDetails = errorDetails;
- mRetryArray = retryArray;
- mNumAttemptsPerFqdn = numAttemptsPerFqdn;
- mUnthrottlingEvents = unthrottlingEvents;
+ abstract @ErrorPolicyErrorType int errorType();
+
+ abstract List<String> errorDetails();
+
+ abstract List<Integer> retryArray();
+
+ abstract Boolean infiniteRetriesWithLastRetryTime();
+
+ abstract List<Integer> unthrottlingEvents();
+
+ abstract Optional<Integer> numAttemptsPerFqdn();
+
+ abstract Optional<Integer> handoverAttemptCount();
+
+ static Builder builder() {
+ return new AutoValue_ErrorPolicyManager_ErrorPolicy.Builder()
+ .setInfiniteRetriesWithLastRetryTime(false);
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setErrorType(int errorType);
+
+ abstract Builder setErrorDetails(List<String> errorDetails);
+
+ abstract Builder setRetryArray(List<Integer> retryArray);
+
+ abstract Builder setInfiniteRetriesWithLastRetryTime(
+ Boolean infiniteRetriesWithLastRetryTime);
+
+ abstract Builder setUnthrottlingEvents(List<Integer> unthrottlingEvents);
+
+ abstract Builder setNumAttemptsPerFqdn(Integer numAttemptsPerFqdn);
+
+ abstract Builder setHandoverAttemptCount(Integer handoverAttemptCount);
+
+ abstract ErrorPolicy build();
}
long getRetryTime(int index) {
long retryTime = -1;
- if (mRetryArray.size() > 0) {
+ if (retryArray().size() > 0) {
// If the index is greater than or equal to the last element's index
// and if the last item in the retryArray is "-1" use the retryTime
// of the element before the last element to repeat the element.
- if (index >= mRetryArray.size() - 1
- && mRetryArray.get(mRetryArray.size() - 1) == -1L) {
- index = mRetryArray.size() - 2;
+ if (infiniteRetriesWithLastRetryTime()) {
+ index = Math.min(index, retryArray().size() - 2);
}
- if (index >= 0 && index < mRetryArray.size()) {
- retryTime = mRetryArray.get(index);
+ if (index >= 0 && index < retryArray().size()) {
+ retryTime = retryArray().get(index);
}
}
@@ -873,33 +937,37 @@
int getCurrentFqdnIndex(int retryIndex, int numFqdns) {
int result = -1;
- if (mNumAttemptsPerFqdn == -1 || mRetryArray.size() <= 0) {
+ if (!numAttemptsPerFqdn().isPresent() || retryArray().size() <= 0) {
return result;
}
// Cycles between 0 and (numFqdns - 1), based on the current attempt count and size of
// mRetryArray.
- return (retryIndex + 1) / mNumAttemptsPerFqdn % numFqdns;
+ return (retryIndex + 1) / numAttemptsPerFqdn().get() % numFqdns;
}
@ErrorPolicyErrorType
int getErrorType() {
- return mErrorType;
+ return errorType();
+ }
+
+ int getHandoverAttemptCount() {
+ return handoverAttemptCount().orElse(Integer.MAX_VALUE);
}
synchronized boolean canUnthrottle(int event) {
- return mUnthrottlingEvents.contains(event);
+ return unthrottlingEvents().contains(event);
}
boolean match(IwlanError iwlanError) {
// Generic by default to match to generic policy.
String iwlanErrorDetail = "*";
- if (mErrorType == FALLBACK_ERROR_TYPE) {
+ if (errorType() == FALLBACK_ERROR_TYPE) {
return true;
- } else if (mErrorType == IKE_PROTOCOL_ERROR_TYPE
+ } else if (errorType() == IKE_PROTOCOL_ERROR_TYPE
&& iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) {
IkeProtocolException exception = (IkeProtocolException) iwlanError.getException();
iwlanErrorDetail = String.valueOf(exception.getErrorType());
- } else if (mErrorType == GENERIC_ERROR_TYPE) {
+ } else if (errorType() == GENERIC_ERROR_TYPE) {
iwlanErrorDetail = getGenericErrorDetailString(iwlanError);
if (iwlanErrorDetail.equals("UNKNOWN")) {
return false;
@@ -909,8 +977,8 @@
}
boolean ret = false;
- for (String errorDetail : mErrorDetails) {
- if (mErrorType == IKE_PROTOCOL_ERROR_TYPE
+ for (String errorDetail : errorDetails()) {
+ if (errorType() == IKE_PROTOCOL_ERROR_TYPE
&& iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION
&& errorDetail.contains("-")) {
// error detail is stored in range format.
@@ -933,15 +1001,22 @@
}
void log() {
- Log.d(LOG_TAG, "ErrorType: " + mErrorType);
- Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(mErrorDetails.toArray()));
- Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(mRetryArray.toArray()));
- Log.d(LOG_TAG, "unthrottlingEvents: " + Arrays.toString(mUnthrottlingEvents.toArray()));
+ Log.d(LOG_TAG, "ErrorType: " + errorType());
+ Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(errorDetails().toArray()));
+ Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(retryArray().toArray()));
+ Log.d(
+ LOG_TAG,
+ "InfiniteRetriesWithLastRetryTime: " + infiniteRetriesWithLastRetryTime());
+ Log.d(
+ LOG_TAG,
+ "UnthrottlingEvents: " + Arrays.toString(unthrottlingEvents().toArray()));
+ Log.d(LOG_TAG, "NumAttemptsPerFqdn: " + numAttemptsPerFqdn());
+ Log.d(LOG_TAG, "handoverAttemptCount: " + handoverAttemptCount());
}
boolean isFallback() {
- if ((mErrorType == FALLBACK_ERROR_TYPE)
- || (mErrorDetails.size() == 1 && mErrorDetails.get(0).equals("*"))) {
+ if ((errorType() == FALLBACK_ERROR_TYPE)
+ || (errorDetails().size() == 1 && errorDetails().get(0).equals("*"))) {
return true;
}
return false;
@@ -1062,6 +1137,15 @@
return ret;
}
+ boolean shouldRetryWithInitialAttach() {
+ // UE should only uses initial attach to reset network failure, not for UE internal or
+ // DNS errors. When the number of handover failures due to network issues exceeds the
+ // configured threshold, UE should request network with initial attach instead of
+ // handover request.
+ return mErrorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE
+ && mCurrentRetryIndex + 1 >= mErrorPolicy.getHandoverAttemptCount();
+ }
+
ErrorPolicy getErrorPolicy() {
return mErrorPolicy;
}
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 8987f7d..d30d99c 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -217,6 +217,7 @@
private EpdgSelector mEpdgSelector;
private IwlanDataTunnelStats mTunnelStats;
private CellInfo mCellInfo = null;
+ private int mCallState = TelephonyManager.CALL_STATE_IDLE;
private long mProcessingStartTime = 0;
// apn to TunnelState
@@ -246,6 +247,7 @@
private boolean mIsHandover;
private Date mBringUpStateTime = null;
private Date mUpStateTime = null;
+ private boolean mIsImsOrEmergency;
public int getPduSessionId() {
return mPduSessionId;
@@ -327,6 +329,14 @@
return mUpStateTime;
}
+ public boolean getIsImsOrEmergency() {
+ return mIsImsOrEmergency;
+ }
+
+ public void setIsImsOrEmergency(boolean isImsOrEmergency) {
+ mIsImsOrEmergency = isImsOrEmergency;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -612,6 +622,7 @@
events.add(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT);
events.add(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT);
events.add(IwlanEventListener.CELLINFO_CHANGED_EVENT);
+ events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT);
IwlanEventListener.getInstance(mContext, slotIndex)
.addEventListener(events, mIwlanDataServiceHandler);
}
@@ -903,13 +914,15 @@
int tunnelStatus,
TunnelLinkProperties linkProperties,
boolean isHandover,
- int pduSessionId) {
+ int pduSessionId,
+ boolean isImsOrEmergency) {
TunnelState tunnelState = new TunnelState(callback);
tunnelState.setState(tunnelStatus);
tunnelState.setProtocolType(dataProfile.getProtocolType());
tunnelState.setTunnelLinkProperties(linkProperties);
tunnelState.setIsHandover(isHandover);
tunnelState.setPduSessionId(pduSessionId);
+ tunnelState.setIsImsOrEmergency(isImsOrEmergency);
mTunnelStateForApn.put(dataProfile.getApn(), tunnelState);
}
@@ -1055,6 +1068,25 @@
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
+ /* Determines if this subscription is in an active call */
+ private boolean isOnCall() {
+ return mCallState != TelephonyManager.CALL_STATE_IDLE;
+ }
+
+ /**
+ * IMS and Emergency are not allowed to retry with initial attach during call to keep call
+ * continuity. Other APNs like XCAP and MMS are allowed to retry with initial attach
+ * regardless of the call state.
+ */
+ private boolean shouldRetryWithInitialAttachForHandoverRequest(
+ String apn, TunnelState tunnelState) {
+ boolean isOnImsOrEmergencyCall = tunnelState.getIsImsOrEmergency() && isOnCall();
+ return tunnelState.getIsHandover()
+ && !isOnImsOrEmergencyCall
+ && ErrorPolicyManager.getInstance(mContext, getSlotIndex())
+ .shouldRetryWithInitialAttach(apn);
+ }
+
/**
* Called when the instance of data service is destroyed (e.g. got unbind or binder died) or
* when the data service provider is removed.
@@ -1168,20 +1200,21 @@
.setId(apnName.hashCode())
.setProtocolType(tunnelState.getProtocolType());
- if (tunnelState.getIsHandover()) {
- respBuilder.setHandoverFailureMode(
- DataCallResponse
- .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
- metricsAtom.setHandoverFailureMode(
- DataCallResponse
- .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
- } else {
+ if (iwlanDataServiceProvider.shouldRetryWithInitialAttachForHandoverRequest(
+ apnName, tunnelState)) {
respBuilder.setHandoverFailureMode(
DataCallResponse
.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
metricsAtom.setHandoverFailureMode(
DataCallResponse
.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ } else if (tunnelState.getIsHandover()) {
+ respBuilder.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
+ metricsAtom.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
}
if (tunnelState.getState()
@@ -1331,6 +1364,13 @@
}
break;
+ case IwlanEventListener.CALL_STATE_CHANGED_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ iwlanDataServiceProvider.mCallState = msg.arg2;
+ break;
+
case EVENT_SETUP_DATA_CALL:
SetupDataCallData setupDataCallData = (SetupDataCallData) msg.obj;
int accessNetworkType = setupDataCallData.mAccessNetworkType;
@@ -1447,10 +1487,8 @@
}
int apnTypeBitmask = dataProfile.getSupportedApnTypesBitmask();
- boolean isIMS = (apnTypeBitmask & ApnSetting.TYPE_IMS) == ApnSetting.TYPE_IMS;
- boolean isEmergency =
- (apnTypeBitmask & ApnSetting.TYPE_EMERGENCY)
- == ApnSetting.TYPE_EMERGENCY;
+ boolean isIMS = hasApnTypes(apnTypeBitmask, ApnSetting.TYPE_IMS);
+ boolean isEmergency = hasApnTypes(apnTypeBitmask, ApnSetting.TYPE_EMERGENCY);
tunnelReqBuilder.setRequestPcscf(isIMS || isEmergency);
tunnelReqBuilder.setIsEmergency(isEmergency);
@@ -1460,7 +1498,8 @@
IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP,
null,
(reason == DataService.REQUEST_REASON_HANDOVER),
- pduSessionId);
+ pduSessionId,
+ isIMS || isEmergency);
boolean result =
iwlanDataServiceProvider
@@ -1990,6 +2029,10 @@
mNetworkMonitorCallback = null;
}
+ boolean hasApnTypes(int apnTypeBitmask, int expectedApn) {
+ return (apnTypeBitmask & expectedApn) != 0;
+ }
+
@VisibleForTesting
void setAppContext(Context appContext) {
mContext = appContext;
@@ -2044,6 +2087,8 @@
return "EVENT_TUNNEL_OPENED_METRICS";
case EVENT_TUNNEL_CLOSED_METRICS:
return "EVENT_TUNNEL_CLOSED_METRICS";
+ case IwlanEventListener.CALL_STATE_CHANGED_EVENT:
+ return "CALL_STATE_CHANGED_EVENT";
default:
return "Unknown(" + event + ")";
}
diff --git a/src/com/google/android/iwlan/IwlanEventListener.java b/src/com/google/android/iwlan/IwlanEventListener.java
index 31f0262..2dd0af3 100644
--- a/src/com/google/android/iwlan/IwlanEventListener.java
+++ b/src/com/google/android/iwlan/IwlanEventListener.java
@@ -85,6 +85,9 @@
/** On Cellinfo changed */
public static final int CELLINFO_CHANGED_EVENT = 11;
+ /** On Call state changed */
+ public static final int CALL_STATE_CHANGED_EVENT = 12;
+
/* Events used and handled by IwlanDataService internally */
public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100;
@@ -106,7 +109,8 @@
CROSS_SIM_CALLING_ENABLE_EVENT,
CROSS_SIM_CALLING_DISABLE_EVENT,
CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT,
- CELLINFO_CHANGED_EVENT
+ CELLINFO_CHANGED_EVENT,
+ CALL_STATE_CHANGED_EVENT
})
@interface IwlanEventType {};
@@ -147,7 +151,7 @@
}
private class RadioInfoTelephonyCallback extends TelephonyCallback
- implements TelephonyCallback.CellInfoListener {
+ implements TelephonyCallback.CellInfoListener, TelephonyCallback.CallStateListener {
@Override
public void onCellInfoChanged(List<CellInfo> arrayCi) {
Log.d(LOG_TAG, "Cellinfo changed");
@@ -160,6 +164,20 @@
}
}
}
+
+ @Override
+ public void onCallStateChanged(int state) {
+ Log.d(
+ LOG_TAG,
+ "Call state changed to " + callStateToString(state) + " for slot " + mSlotId);
+
+ for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
+ IwlanEventListener instance = entry.getValue();
+ if (instance != null) {
+ instance.updateHandlers(CALL_STATE_CHANGED_EVENT, state);
+ }
+ }
+ }
}
/**
@@ -515,4 +533,26 @@
}
}
}
+
+ private synchronized void updateHandlers(int event, int state) {
+ if (eventHandlers.contains(event)) {
+ Log.d(SUB_TAG, "Updating handlers for the event: " + event);
+ for (Handler handler : eventHandlers.get(event)) {
+ handler.obtainMessage(event, mSlotId, state).sendToTarget();
+ }
+ }
+ }
+
+ private String callStateToString(int state) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ return "CALL_STATE_IDLE";
+ case TelephonyManager.CALL_STATE_RINGING:
+ return "CALL_STATE_RINGING";
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ return "CALL_STATE_OFFHOOK";
+ default:
+ return "Unknown Call State (" + state + ")";
+ }
+ }
}
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index a4539e5..0895f09 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
@@ -40,6 +41,9 @@
import androidx.test.InstrumentationRegistry;
+import com.google.auto.value.AutoValue;
+
+import org.json.JSONArray;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -49,9 +53,80 @@
import org.mockito.quality.Strictness;
import java.io.InputStream;
+import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class ErrorPolicyManagerTest {
+ @AutoValue
+ abstract static class ErrorPolicyString {
+ abstract String errorType();
+
+ abstract List<String> errorDetails();
+
+ abstract List<String> retryArray();
+
+ abstract List<String> unthrottlingEvents();
+
+ abstract Optional<String> numAttemptsPerFqdn();
+
+ abstract Optional<String> handoverAttemptCount();
+
+ static Builder builder() {
+ return new AutoValue_ErrorPolicyManagerTest_ErrorPolicyString.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setErrorType(String errorType);
+
+ abstract Builder setErrorDetails(List<String> errorDetails);
+
+ abstract Builder setRetryArray(List<String> retryArray);
+
+ abstract Builder setUnthrottlingEvents(List<String> unthrottlingEvents);
+
+ abstract Builder setNumAttemptsPerFqdn(String numAttemptsPerFqdn);
+
+ abstract Builder setHandoverAttemptCount(String handoverAttemptCount);
+
+ abstract ErrorPolicyString build();
+ }
+
+ String getErrorPolicyInString() {
+ StringBuilder errorPolicy =
+ new StringBuilder(
+ "\"ErrorType\": \""
+ + errorType()
+ + "\","
+ + "\"ErrorDetails\": [\""
+ + String.join("\", \"", errorDetails())
+ + "\"],"
+ + "\"RetryArray\": [\""
+ + String.join("\", \"", retryArray())
+ + "\"],"
+ + "\"UnthrottlingEvents\": [\""
+ + String.join("\", \"", unthrottlingEvents())
+ + "\"]");
+
+ numAttemptsPerFqdn()
+ .ifPresent(
+ numAttemptsPerFqdn ->
+ errorPolicy
+ .append(",\"NumAttemptsPerFqdn\": \"")
+ .append(numAttemptsPerFqdn)
+ .append("\""));
+ handoverAttemptCount()
+ .ifPresent(
+ handoverAttemptCount ->
+ errorPolicy
+ .append(",\"HandoverAttemptCount\": \"")
+ .append(handoverAttemptCount)
+ .append("\""));
+ return errorPolicy.toString();
+ }
+ }
+
private static final String TAG = "ErrorPolicyManagerTest";
// @Rule public final MockitoRule mockito = MockitoJUnit.rule();
@@ -131,17 +206,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34", "9000-9050"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34", "9000-9050"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -207,11 +287,14 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"WRONG_ERROR_DETAIL"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("WRONG_ERROR_DETAIL"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -264,17 +347,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"*"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("*"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -317,17 +405,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -368,17 +461,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -414,17 +512,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"6", "12", "24"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("6", "12", "24"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -465,17 +568,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"6", "12", "24"},
- new String[] {"WIFI_CALLING_DISABLE_EVENT", "WIFI_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("6", "12", "24"))
+ .setUnthrottlingEvents(
+ List.of("WIFI_CALLING_DISABLE_EVENT", "WIFI_DISABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -516,17 +624,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -568,17 +681,22 @@
+ apn1
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -624,17 +742,22 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"10", "15", "20"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("10", "15", "20"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -687,19 +810,21 @@
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE", /* ErrorType */
- new String[] {"15500"}, /* ErrorDetails ("CONGESTION") */
- "6", /* NumAttemptsPerFqdn */
- new String[] {
- "0", "0", "300", "600", "1200", "0", "0", "0", "300", "600",
- "1200", "-1"
- }, /* RetryArray */
- new String[] {
- "APM_ENABLE_EVENT",
- "WIFI_DISABLE_EVENT",
- "WIFI_CALLING_DISABLE_EVENT"
- }) /* UnthrottlingEvents */
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("15500")) /* CONGESTION */
+ .setRetryArray(
+ List.of(
+ "0", "0", "300", "600", "1200", "0", "0", "0",
+ "300", "600", "1200", "-1"))
+ .setUnthrottlingEvents(
+ List.of(
+ "APM_ENABLE_EVENT",
+ "WIFI_DISABLE_EVENT",
+ "WIFI_CALLING_DISABLE_EVENT"))
+ .setNumAttemptsPerFqdn("6")
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -758,6 +883,124 @@
}
@Test
+ public void testShouldRetryWithInitialAttach() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ // IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
+ IwlanError iwlanError = buildIwlanIkeAuthFailedError();
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(4, time);
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(8, time);
+ // Reached handover attempt count and error is IKE protocol error
+ assertTrue(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+ }
+
+ @Test
+ public void testShouldRetryWithInitialAttachForInternalError() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}, {"
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0", "0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ // GENERIC_PROTOCOL_ERROR_TYPE - SERVER_SELECTION_FAILED and retryArray = 0, 0
+ IwlanError iwlanError = new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED);
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ // Should not retry with initial attach as the errors are not IKE_PROTOCOL_ERROR_TYPE
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+ }
+
+ @Test
+ public void testHandoverAttemptCountInvalidErrorType() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("*"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mErrorPolicyManager.readErrorPolicies(new JSONArray(config)));
+ }
+
+ @Test
public void testErrorStats() throws Exception {
String apn1 = "ims";
String apn2 = "mms";
@@ -798,48 +1041,6 @@
assertEquals(resultServerApn2, serverSelectionCountApn2);
}
- private String getErrorTypeInJSON(
- String ErrorType,
- String[] errorDetails,
- String[] retryArray,
- String[] unthrottlingEvents) {
- return "\"ErrorType\": \""
- + ErrorType
- + "\","
- + "\"ErrorDetails\": [\""
- + String.join("\", \"", errorDetails)
- + "\"],"
- + "\"RetryArray\": [\""
- + String.join("\", \"", retryArray)
- + "\"],"
- + "\"UnthrottlingEvents\": [\""
- + String.join("\", \"", unthrottlingEvents)
- + "\"]";
- }
-
- private String getErrorTypeInJSON(
- String ErrorType,
- String[] errorDetails,
- String numAttemptsPerFqdn,
- String[] retryArray,
- String[] unthrottlingEvents) {
- return "\"ErrorType\": \""
- + ErrorType
- + "\","
- + "\"ErrorDetails\": [\""
- + String.join("\", \"", errorDetails)
- + "\"],"
- + "\"NumAttemptsPerFqdn\": \""
- + numAttemptsPerFqdn
- + "\","
- + "\"RetryArray\": [\""
- + String.join("\", \"", retryArray)
- + "\"],"
- + "\"UnthrottlingEvents\": [\""
- + String.join("\", \"", unthrottlingEvents)
- + "\"]";
- }
-
private void advanceClockByTimeMs(long time) {
mMockedClockTime += time;
mTestLooper.dispatchAll();
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index ed93c90..2446655 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -348,7 +348,7 @@
@Test
public void testRequestDataCallListPass() throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
List<LinkAddress> mInternalAddressList;
List<InetAddress> mDNSAddressList;
List<InetAddress> mGatewayAddressList;
@@ -363,8 +363,9 @@
new DataServiceCallback(callback),
TunnelState.TUNNEL_UP,
mLinkProperties,
- false,
- 1);
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
mTestLooper.dispatchAll();
latch.await(1, TimeUnit.SECONDS);
@@ -441,7 +442,7 @@
@Test
public void testIwlanSetupDataCallWithIllegalState() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* Wifi is not connected */
mIwlanDataService.setNetworkConnected(
@@ -481,7 +482,7 @@
@Test
public void testIwlanSetupDataCallWithBringUpTunnel() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* Wifi is connected */
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
@@ -521,7 +522,7 @@
@Test
public void testSliceInfoInclusionInDataCallResponse() throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* Wifi is connected */
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
@@ -569,14 +570,20 @@
@Test
public void testIwlanDeactivateDataCallWithCloseTunnel() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
verifyNetworkConnected(TRANSPORT_WIFI);
mSpyIwlanDataServiceProvider.setTunnelState(
- dp, mMockDataServiceCallback, TunnelState.TUNNEL_IN_BRINGUP, null, false, 1);
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
@@ -597,14 +604,20 @@
@Test
public void testIwlanDeactivateDataCallAfterSuccessHandover() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
verifyNetworkConnected(TRANSPORT_WIFI);
mSpyIwlanDataServiceProvider.setTunnelState(
- dp, mMockDataServiceCallback, TunnelState.TUNNEL_IN_BRINGUP, null, false, 1);
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
@@ -624,8 +637,8 @@
}
@Test
- public void testHandoverFailureModeNormal() {
- DataProfile dp = buildDataProfile();
+ public void testHandoverFailureModeDefault() {
+ DataProfile dp = buildImsDataProfile();
int setupDataReason = DataService.REQUEST_REASON_NORMAL;
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
@@ -638,9 +651,10 @@
dp,
mMockDataServiceCallback,
TunnelState.TUNNEL_IN_BRINGUP,
- null,
+ null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
- 1);
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -667,14 +681,14 @@
DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
assertEquals(
dataCallResponse.getHandoverFailureMode(),
- DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY);
assertEquals(dataCallResponse.getCause(), DataFailCause.USER_AUTHENTICATION);
assertEquals(dataCallResponse.getRetryDurationMillis(), 5L);
}
@Test
public void testHandoverFailureModeHandover() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
@@ -682,14 +696,17 @@
when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
.thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(false);
mSpyIwlanDataServiceProvider.setTunnelState(
dp,
mMockDataServiceCallback,
TunnelState.TUNNEL_IN_BRINGUP,
- null,
+ null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
- 1);
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -722,6 +739,238 @@
}
@Test
+ public void testSupportInitialAttachSuccessOnIms() {
+ DataProfile dp = buildImsDataProfile();
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = IMS, in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_IDLE)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // Not on video or voice call
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testSupportInitialAttachSuccessOnEmergency() {
+ DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = Emergency, in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_IDLE)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 512, // type Emergency
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // Not on video or voice call
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testSupportInitialAttachOnImsCall() {
+ DataProfile dp = buildImsDataProfile();
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = IMS, in call
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_OFFHOOK)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null /* linkProperties */,
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // In call state
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testSupportInitialAttachOnEmergencyCall() {
+ DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = Emergency, in call
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_OFFHOOK)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null /* linkProperties */,
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 512, // type Emergency
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // In call state
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
public void testDnsPrefetching() throws Exception {
IwlanNetworkMonitorCallback mNetworkMonitorCallback =
mIwlanDataService.getNetworkMonitorCallback();
@@ -785,7 +1034,11 @@
mTestLooper.dispatchAll();
}
- private DataProfile buildDataProfile() {
+ private DataProfile buildImsDataProfile() {
+ return buildDataProfile(ApnSetting.TYPE_IMS);
+ }
+
+ private DataProfile buildDataProfile(int supportedApnTypesBitmask) {
DataProfile dp =
new DataProfile.Builder()
.setProfileId(1)
@@ -799,7 +1052,7 @@
// .setMaxConnections(3)
// .setWaitTime(10)
.enable(true)
- .setSupportedApnTypesBitmask(ApnSetting.TYPE_IMS)
+ .setSupportedApnTypesBitmask(supportedApnTypesBitmask)
.setRoamingProtocolType(ApnSetting.PROTOCOL_IPV4V6) // IPv4v6
.setBearerBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
.setPersistent(true)
@@ -822,7 +1075,7 @@
@Test
public void testIwlanSetupDataCallFailsWithCellularAndCstDisabled() throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* CST is disabled, and data is on the same sub as the data service provider */
when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
@@ -853,7 +1106,7 @@
@Test
public void testIwlanSetupDataCallFailsWithCellularOnSameSubAndCstEnabled() throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* CST is enabled, but data is on the same sub as the DataServiceProvider */
when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
@@ -886,7 +1139,7 @@
@Test
public void testIwlanSetupDataCallSucceedsWithCellularOnDifferentSubAndCstEnabled()
throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
/* CST is enabled, but data is on the same sub as the DataServiceProvider */
when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
@@ -931,7 +1184,7 @@
@Test
public void testIwlanTunnelStatsFailureCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
@@ -952,7 +1205,7 @@
@Test
public void testIwlanTunnelStatsUnsolDownCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
.thenReturn(mMockErrorPolicyManager);
@@ -976,7 +1229,7 @@
@Test
public void testIwlanTunnelStats() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
@@ -1024,10 +1277,16 @@
@Test
public void testIwlanDataServiceHandlerOnUnbind() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
mSpyIwlanDataServiceProvider.setTunnelState(
- dp, mMockDataServiceCallback, TunnelState.TUNNEL_UP, null, false, 1);
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_UP,
+ null /* linkProperties */,
+ false /* isHandover */,
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
diff --git a/test/com/google/android/iwlan/IwlanEventListenerTest.java b/test/com/google/android/iwlan/IwlanEventListenerTest.java
index f154f20..509c0da 100644
--- a/test/com/google/android/iwlan/IwlanEventListenerTest.java
+++ b/test/com/google/android/iwlan/IwlanEventListenerTest.java
@@ -93,8 +93,7 @@
when(mMockContext.getSystemService(eq(SubscriptionManager.class)))
.thenReturn(mMockSubscriptionManager);
- when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
- anyInt()))
+ when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt()))
.thenReturn(mMockSubscriptionInfo);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
@@ -282,4 +281,25 @@
verify(mMockMessage, times(1)).sendToTarget();
}
+
+ @Test
+ public void testCallStateChanged() throws Exception {
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CALL_STATE_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ eq(TelephonyManager.CALL_STATE_OFFHOOK)))
+ .thenReturn(mMockMessage);
+
+ events = new ArrayList<Integer>();
+ events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT);
+ mIwlanEventListener.addEventListener(events, mMockHandler);
+
+ mIwlanEventListener.registerTelephonyCallback();
+
+ TelephonyCallback.CallStateListener mTelephonyCallback =
+ mIwlanEventListener.getTelephonyCallback();
+ mTelephonyCallback.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ verify(mMockMessage, times(1)).sendToTarget();
+ }
}