Support initial attach for HO failures due to network issues
- This feature is only used when handover request fails due to network error, i.e. IKE_PROTOCOL_ERROR_TYPE.
- In idle state, or not on voice / video call, Iwlan set handover failure mode as NO_FALLBACK_RETRY_SETUP_NORMAL if attempt count reach the configured threshold. Otherwise, to keep call continuity, Iwlan will always set handover failure mode as NO_FALLBACK_RETRY_HANDOVER.
Bug: 226938560
Test: atest IwlanTests
Change-Id: I1099e708af74aaea13185a03bdb53757082d304c
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index cdf0c6e..6d64814 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -447,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()) {
@@ -1124,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 1bdebe5..b9dd851 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);
}
@@ -1043,6 +1056,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.
@@ -1156,20 +1188,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()
@@ -1319,6 +1352,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;
@@ -1435,10 +1475,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);
@@ -1448,7 +1486,8 @@
IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP,
null,
(reason == DataService.REQUEST_REASON_HANDOVER),
- pduSessionId);
+ pduSessionId,
+ isIMS || isEmergency);
boolean result =
iwlanDataServiceProvider
@@ -1978,6 +2017,10 @@
mNetworkMonitorCallback = null;
}
+ boolean hasApnTypes(int apnTypeBitmask, int expectedApn) {
+ return (apnTypeBitmask & expectedApn) != 0;
+ }
+
@VisibleForTesting
void setAppContext(Context appContext) {
mContext = appContext;
@@ -2032,6 +2075,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 6116475..0895f09 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -883,6 +883,98 @@
}
@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 =
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index bd74d80..de68306 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();
@@ -783,7 +1032,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)
@@ -797,7 +1050,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)
@@ -820,7 +1073,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);
@@ -851,7 +1104,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);
@@ -884,7 +1137,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);
@@ -929,7 +1182,7 @@
@Test
public void testIwlanTunnelStatsFailureCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
@@ -950,7 +1203,7 @@
@Test
public void testIwlanTunnelStatsUnsolDownCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
.thenReturn(mMockErrorPolicyManager);
@@ -974,7 +1227,7 @@
@Test
public void testIwlanTunnelStats() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
@@ -1022,10 +1275,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();
+ }
}