summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flags/main.aconfig13
-rw-r--r--src/com/google/android/iwlan/ErrorPolicyManager.java1
-rw-r--r--src/com/google/android/iwlan/IwlanDataService.java164
-rw-r--r--src/com/google/android/iwlan/IwlanEventListener.java51
-rw-r--r--src/com/google/android/iwlan/epdg/EpdgSelector.java106
-rw-r--r--src/com/google/android/iwlan/epdg/EpdgTunnelManager.java102
-rw-r--r--src/com/google/android/iwlan/epdg/TunnelSetupRequest.java7
-rw-r--r--test/com/google/android/iwlan/ErrorPolicyManagerTest.java59
-rw-r--r--test/com/google/android/iwlan/IwlanDataServiceTest.java355
-rw-r--r--test/com/google/android/iwlan/IwlanEventListenerTest.java72
-rw-r--r--test/com/google/android/iwlan/epdg/EpdgSelectorTest.java178
-rw-r--r--test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java113
12 files changed, 1097 insertions, 124 deletions
diff --git a/flags/main.aconfig b/flags/main.aconfig
index 6749d7f..57b0527 100644
--- a/flags/main.aconfig
+++ b/flags/main.aconfig
@@ -1,4 +1,5 @@
package: "com.google.android.iwlan.flags"
+container: "system"
flag {
name: "prevent_epdg_selection_threads_exhausted"
@@ -24,3 +25,15 @@ flag {
description: "Reorder IKE and Child SA security transforms with high secured as prioritized"
bug: "306323917"
}
+flag {
+ name: "epdg_selection_exclude_failed_ip_address"
+ namespace: "iwlan_telephony"
+ description: "Exclude the failed ip address in epdg selection until tunnel establish successfully or all ip address candidates are failed"
+ bug: "300026897"
+}
+flag {
+ name: "update_n1_mode_on_ui_change"
+ namespace: "iwlan_telephony"
+ description: "Enables a 5G SA capable UE to re-establish PDN connections over Wi-Fi with/without N1_MODE_CAPABILITY, when the user enables/disables 5G via UI/UX."
+ bug: "273384888"
+}
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index bef25ec..290283a 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -1372,6 +1372,7 @@ public class ErrorPolicyManager {
case IwlanEventListener.APM_DISABLE_EVENT:
case IwlanEventListener.WIFI_DISABLE_EVENT:
case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT:
+ case IwlanEventListener.WIFI_AP_CHANGED_EVENT:
unthrottleLastErrorOnEvent(msg.what);
break;
default:
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 58d000c..cd43ece 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -18,6 +18,7 @@ package com.google.android.iwlan;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.ipsec.ike.ike3gpp.Ike3gppParams.PDU_SESSION_ID_UNSET;
import android.content.Context;
import android.content.Intent;
@@ -66,6 +67,8 @@ import com.google.android.iwlan.epdg.EpdgSelector;
import com.google.android.iwlan.epdg.EpdgTunnelManager;
import com.google.android.iwlan.epdg.TunnelLinkProperties;
import com.google.android.iwlan.epdg.TunnelSetupRequest;
+import com.google.android.iwlan.flags.FeatureFlags;
+import com.google.android.iwlan.flags.FeatureFlagsImpl;
import com.google.android.iwlan.proto.MetricsAtom;
import java.io.FileDescriptor;
@@ -75,6 +78,7 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@@ -86,6 +90,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class IwlanDataService extends DataService {
+ private final FeatureFlags mFeatureFlags;
private static final String TAG = IwlanDataService.class.getSimpleName();
private static final String CONTEXT_ATTRIBUTION_TAG = "IWLAN";
@@ -126,6 +131,17 @@ public class IwlanDataService extends DataService {
private static Transport sDefaultDataTransport = Transport.UNSPECIFIED_NETWORK;
+ private boolean mIs5GEnabledOnUi;
+
+ public IwlanDataService() {
+ mFeatureFlags = new FeatureFlagsImpl();
+ }
+
+ @VisibleForTesting
+ IwlanDataService(FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
+ }
+
// TODO: see if network monitor callback impl can be shared between dataservice and
// networkservice
// This callback runs in the same thread as IwlanDataServiceHandler
@@ -256,6 +272,15 @@ public class IwlanDataService extends DataService {
private Date mUpStateTime = null;
private boolean mIsImsOrEmergency;
private DeactivateDataCallData mPendingDeactivateDataCallData;
+ private boolean mIsDataCallWithN1;
+
+ public boolean getIsDataCallWithN1() {
+ return mIsDataCallWithN1;
+ }
+
+ public void setIsDataCallWithN1(boolean mIsDataCallWithN1) {
+ this.mIsDataCallWithN1 = mIsDataCallWithN1;
+ }
public int getPduSessionId() {
return mPduSessionId;
@@ -613,6 +638,7 @@ public class IwlanDataService extends DataService {
events.add(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT);
events.add(IwlanEventListener.CELLINFO_CHANGED_EVENT);
events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT);
+ events.add(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT);
IwlanEventListener.getInstance(mContext, slotIndex)
.addEventListener(events, getIwlanDataServiceHandler());
}
@@ -902,14 +928,21 @@ public class IwlanDataService extends DataService {
}
}
- void forceCloseTunnels() {
+ /**
+ * Closes all tunnels forcefully for a specified reason.
+ *
+ * @param reason The reason for closing the tunnel. Must be {@link
+ * EpdgTunnelManager.TunnelBringDownReason}.
+ */
+ void forceCloseTunnels(@EpdgTunnelManager.TunnelBringDownReason int reason) {
for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
getTunnelManager()
.closeTunnel(
entry.getKey(),
true /* forceClose */,
getIwlanTunnelCallback(),
- getIwlanTunnelMetrics());
+ getIwlanTunnelMetrics(),
+ reason);
}
}
@@ -930,14 +963,15 @@ public class IwlanDataService extends DataService {
}
@VisibleForTesting
- void setTunnelState(
+ protected void setTunnelState(
DataProfile dataProfile,
DataServiceCallback callback,
int tunnelStatus,
TunnelLinkProperties linkProperties,
boolean isHandover,
int pduSessionId,
- boolean isImsOrEmergency) {
+ boolean isImsOrEmergency,
+ boolean isDataCallSetupWithN1) {
TunnelState tunnelState = new TunnelState(callback);
tunnelState.setState(tunnelStatus);
tunnelState.setProtocolType(dataProfile.getApnSetting().getProtocol());
@@ -945,6 +979,7 @@ public class IwlanDataService extends DataService {
tunnelState.setIsHandover(isHandover);
tunnelState.setPduSessionId(pduSessionId);
tunnelState.setIsImsOrEmergency(isImsOrEmergency);
+ tunnelState.setIsDataCallWithN1(isDataCallSetupWithN1);
mTunnelStateForApn.put(dataProfile.getApnSetting().getApnName(), tunnelState);
}
@@ -1169,6 +1204,60 @@ public class IwlanDataService extends DataService {
public void setCalendar(Calendar c) {
mCalendar = c;
}
+
+ private boolean isPdnReestablishNeededOnIdleN1Update() {
+ return isN1ModeSupported() && (needIncludeN1ModeCapability() != mIs5GEnabledOnUi);
+ }
+
+ private void disconnectPdnForN1ModeUpdate() {
+ if (hasActiveOrInitiatingDataCall()) {
+ forceCloseTunnels(
+ mIs5GEnabledOnUi
+ ? EpdgTunnelManager.BRINGDOWN_REASON_ENABLE_N1_MODE
+ : EpdgTunnelManager.BRINGDOWN_REASON_DISABLE_N1_MODE);
+ }
+ }
+
+ private boolean hasActiveOrInitiatingDataCall() {
+ return mTunnelStateForApn.values().stream()
+ .anyMatch(
+ tunnelState ->
+ tunnelState.getState() == TunnelState.TUNNEL_UP
+ || tunnelState.getState()
+ == TunnelState.TUNNEL_IN_BRINGUP);
+ }
+
+ // TODO(b/309867756): Include N1_MODE_CAPABILITY inclusion status in metrics.
+ private boolean needIncludeN1ModeCapability() {
+ if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+ return isN1ModeSupported();
+ }
+ if (!isN1ModeSupported()) {
+ return false;
+ }
+ // Maintain uniform N1_MODE_CAPABILITY Notify inclusion for all PDNs.
+ // Initiate PDN with current N1 inclusion in tunnel_up or tunnel_in_bringup states;
+ // otherwise, use UI settings.
+ return hasActiveOrInitiatingDataCall() ? isDataCallSetupWithN1() : mIs5GEnabledOnUi;
+ }
+
+ private boolean isDataCallSetupWithN1() {
+ return mTunnelStateForApn.values().stream().anyMatch(TunnelState::getIsDataCallWithN1);
+ }
+
+ protected boolean isN1ModeSupported() {
+ int[] nrAvailabilities =
+ IwlanHelper.getConfig(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ mContext,
+ getSlotIndex());
+ Log.d(
+ TAG,
+ "KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY : "
+ + Arrays.toString(nrAvailabilities));
+ return Arrays.stream(nrAvailabilities)
+ .anyMatch(k -> k == CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA);
+ }
}
private final class IwlanDataServiceHandler extends Handler {
@@ -1437,7 +1526,29 @@ public class IwlanDataService extends DataService {
iwlanDataServiceProvider =
(IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
- iwlanDataServiceProvider.mCallState = msg.arg2;
+ int previousCallState = iwlanDataServiceProvider.mCallState;
+ int currentCallState = iwlanDataServiceProvider.mCallState = msg.arg2;
+
+ if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+ break;
+ }
+
+ // Disconnect PDN if call ends and re-establishment needed.
+ if (previousCallState != currentCallState
+ && currentCallState == TelephonyManager.CALL_STATE_IDLE
+ && iwlanDataServiceProvider.isPdnReestablishNeededOnIdleN1Update()) {
+ iwlanDataServiceProvider.disconnectPdnForN1ModeUpdate();
+ }
+ break;
+
+ case IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT:
+ if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+ break;
+ }
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+ long allowedNetworkType = (long) msg.obj;
+ onPreferredNetworkTypeChanged(iwlanDataServiceProvider, allowedNetworkType);
break;
case EVENT_SETUP_DATA_CALL:
@@ -1454,6 +1565,7 @@ public class IwlanDataService extends DataService {
if ((accessNetworkType != AccessNetworkType.IWLAN)
|| (dataProfile == null)
+ || (dataProfile.getApnSetting() == null)
|| (linkProperties == null
&& reason == DataService.REQUEST_REASON_HANDOVER)) {
@@ -1501,8 +1613,7 @@ public class IwlanDataService extends DataService {
dataProfile.getApnSetting().getApnName());
// Return the existing PDN if the pduSessionId is the same and the tunnel
- // state is
- // TUNNEL_UP.
+ // state is TUNNEL_UP.
if (tunnelState != null) {
if (tunnelState.getPduSessionId() == pduSessionId
&& tunnelState.getState()
@@ -1542,11 +1653,16 @@ public class IwlanDataService extends DataService {
return;
}
+ boolean isDataCallSetupWithN1 =
+ iwlanDataServiceProvider.needIncludeN1ModeCapability();
+
TunnelSetupRequest.Builder tunnelReqBuilder =
TunnelSetupRequest.builder()
.setApnName(dataProfile.getApnSetting().getApnName())
.setIsRoaming(isRoaming)
- .setPduSessionId(pduSessionId)
+ .setPduSessionId(
+ isDataCallSetupWithN1
+ ? pduSessionId : PDU_SESSION_ID_UNSET)
.setApnIpProtocol(
isRoaming
? dataProfile
@@ -1581,7 +1697,8 @@ public class IwlanDataService extends DataService {
null,
(reason == DataService.REQUEST_REASON_HANDOVER),
pduSessionId,
- isIMS || isEmergency);
+ isIMS || isEmergency,
+ isDataCallSetupWithN1);
boolean result =
iwlanDataServiceProvider
@@ -1623,7 +1740,7 @@ public class IwlanDataService extends DataService {
case EVENT_FORCE_CLOSE_TUNNEL:
for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
- dp.forceCloseTunnels();
+ dp.forceCloseTunnels(EpdgTunnelManager.BRINGDOWN_REASON_UNKNOWN);
}
break;
@@ -2165,11 +2282,37 @@ public class IwlanDataService extends DataService {
return "EVENT_DEACTIVATE_DATA_CALL_WITH_DELAY";
case IwlanEventListener.CALL_STATE_CHANGED_EVENT:
return "CALL_STATE_CHANGED_EVENT";
+ case IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT:
+ return "PREFERRED_NETWORK_TYPE_CHANGED_EVENT";
default:
return "Unknown(" + event + ")";
}
}
+ private void initAllowedNetworkType() {
+ TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mIs5GEnabledOnUi =
+ ((mTelephonyManager.getAllowedNetworkTypesBitmask()
+ & TelephonyManager.NETWORK_TYPE_BITMASK_NR)
+ != 0);
+ }
+
+ private void onPreferredNetworkTypeChanged(
+ IwlanDataServiceProvider iwlanDataServiceProvider, long allowedNetworkType) {
+ boolean isCurrentUiEnable5G =
+ (allowedNetworkType & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0;
+ boolean isPreviousUiEnable5G = mIs5GEnabledOnUi;
+ mIs5GEnabledOnUi = isCurrentUiEnable5G;
+ if (!iwlanDataServiceProvider.isN1ModeSupported()) {
+ return;
+ }
+ if (isPreviousUiEnable5G != isCurrentUiEnable5G) {
+ if (!iwlanDataServiceProvider.isOnCall()) {
+ iwlanDataServiceProvider.disconnectPdnForN1ModeUpdate();
+ }
+ }
+ }
+
@Override
public void onCreate() {
Context context = getApplicationContext().createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
@@ -2177,6 +2320,7 @@ public class IwlanDataService extends DataService {
IwlanBroadcastReceiver.startListening(mContext);
IwlanCarrierConfigChangeListener.startListening(mContext);
IwlanHelper.startCountryDetector(mContext);
+ initAllowedNetworkType();
}
@Override
diff --git a/src/com/google/android/iwlan/IwlanEventListener.java b/src/com/google/android/iwlan/IwlanEventListener.java
index 114c66e..890afb4 100644
--- a/src/com/google/android/iwlan/IwlanEventListener.java
+++ b/src/com/google/android/iwlan/IwlanEventListener.java
@@ -39,6 +39,9 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.google.android.iwlan.flags.FeatureFlags;
+import com.google.android.iwlan.flags.FeatureFlagsImpl;
+
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -48,6 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class IwlanEventListener {
+ private final FeatureFlags mFeatureFlags;
public static final int UNKNOWN_EVENT = -1;
/** On {@link IwlanCarrierConfigChangeListener#onCarrierConfigChanged} is called. */
@@ -88,6 +92,9 @@ public class IwlanEventListener {
/** On Call state changed */
public static final int CALL_STATE_CHANGED_EVENT = 12;
+ /** On Preferred Network Type changed */
+ public static final int PREFERRED_NETWORK_TYPE_CHANGED_EVENT = 13;
+
/* Events used and handled by IwlanDataService internally */
public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100;
@@ -106,7 +113,8 @@ public class IwlanEventListener {
CROSS_SIM_CALLING_DISABLE_EVENT,
CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT,
CELLINFO_CHANGED_EVENT,
- CALL_STATE_CHANGED_EVENT
+ CALL_STATE_CHANGED_EVENT,
+ PREFERRED_NETWORK_TYPE_CHANGED_EVENT,
})
@interface IwlanEventType {}
@@ -148,7 +156,9 @@ public class IwlanEventListener {
}
private class RadioInfoTelephonyCallback extends TelephonyCallback
- implements TelephonyCallback.CellInfoListener, TelephonyCallback.CallStateListener {
+ implements TelephonyCallback.CellInfoListener,
+ TelephonyCallback.CallStateListener,
+ TelephonyCallback.AllowedNetworkTypesListener {
@Override
public void onCellInfoChanged(List<CellInfo> arrayCi) {
Log.d(LOG_TAG, "Cellinfo changed");
@@ -172,13 +182,32 @@ public class IwlanEventListener {
instance.updateHandlers(CALL_STATE_CHANGED_EVENT, state);
}
}
+
+ @Override
+ public void onAllowedNetworkTypesChanged(
+ @TelephonyManager.AllowedNetworkTypesReason int reason,
+ @TelephonyManager.NetworkTypeBitMask long allowedNetworkType) {
+ if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+ return;
+ }
+
+ if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) {
+ return;
+ }
+
+ IwlanEventListener instance = mInstances.get(mSlotId);
+ if (instance != null) {
+ instance.updateHandlers(PREFERRED_NETWORK_TYPE_CHANGED_EVENT, allowedNetworkType);
+ }
+ }
}
/**
* Returns IwlanEventListener instance
*/
public static IwlanEventListener getInstance(@NonNull Context context, int slotId) {
- return mInstances.computeIfAbsent(slotId, k -> new IwlanEventListener(context, slotId));
+ return mInstances.computeIfAbsent(
+ slotId, k -> new IwlanEventListener(context, slotId, new FeatureFlagsImpl()));
}
@VisibleForTesting
@@ -371,16 +400,20 @@ public class IwlanEventListener {
case "CELLINFO_CHANGED_EVENT":
ret = CELLINFO_CHANGED_EVENT;
break;
+ case "PREFERRED_NETWORK_TYPE_CHANGED_EVENT":
+ ret = PREFERRED_NETWORK_TYPE_CHANGED_EVENT;
+ break;
}
return ret;
}
- private IwlanEventListener(Context context, int slotId) {
+ IwlanEventListener(Context context, int slotId, FeatureFlags featureFlags) {
mContext = context;
mSlotId = slotId;
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
SUB_TAG = IwlanEventListener.class.getSimpleName() + "[" + slotId + "]";
sIsAirplaneModeOn = null;
+ mFeatureFlags = featureFlags;
}
private void onCarrierConfigChanged(int subId, int carrierId) {
@@ -540,6 +573,16 @@ public class IwlanEventListener {
}
}
+ private synchronized void updateHandlers(int event, long allowedNetworkType) {
+ if (eventHandlers.contains(event)) {
+ Log.d(SUB_TAG, "Updating handlers for the event: " + event);
+ for (Handler handler : eventHandlers.get(event)) {
+ handler.obtainMessage(event, mSlotId, 0 /* unused */, allowedNetworkType)
+ .sendToTarget();
+ }
+ }
+ }
+
private String callStateToString(int state) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index e55467b..c231d50 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -83,6 +83,10 @@ public class EpdgSelector {
private List<byte[]> mV6PcoData;
@NonNull private final ErrorPolicyManager mErrorPolicyManager;
+ // Temporary excluded IP addresses due to recent failures. Cleared after tunnel opened
+ // successfully or all resolved IP addresses are tried and excluded.
+ private final Set<InetAddress> mTemporaryExcludedAddresses;
+
// The default DNS timeout in the DNS module is set to 5 seconds. To account for IPC overhead,
// IWLAN applies an internal timeout of 6 seconds, slightly longer than the default timeout
private static final long DNS_RESOLVER_TIMEOUT_DURATION_SEC = 6L;
@@ -153,6 +157,8 @@ public class EpdgSelector {
mV6PcoData = new ArrayList<>();
mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId);
+
+ mTemporaryExcludedAddresses = new HashSet<>();
initializeExecutors();
}
@@ -235,6 +241,45 @@ public class EpdgSelector {
mV6PcoData.clear();
}
+ /**
+ * Notify {@link EpdgSelector} that ePDG is connected successfully. The excluded ip addresses
+ * will be cleared so that next ePDG selection will retry all ip addresses.
+ */
+ void onEpdgConnectedSuccessfully() {
+ clearExcludedIpAddresses();
+ }
+
+ /**
+ * Notify {@link EpdgSelector} that failed to connect to an ePDG. EpdgSelector will add the
+ * {@code ipAddress} into excluded list and will not retry until any ePDG connected successfully
+ * or all ip addresses candidates are tried.
+ *
+ * @param ipAddress the ePDG ip address that failed to connect
+ */
+ void onEpdgConnectionFailed(InetAddress ipAddress) {
+ excludeIpAddress(ipAddress);
+ }
+
+ private void excludeIpAddress(InetAddress ipAddress) {
+ if (!mFeatureFlags.epdgSelectionExcludeFailedIpAddress()) {
+ return;
+ }
+ Log.d(TAG, "Added " + ipAddress + " into temporary excluded addresses");
+ mTemporaryExcludedAddresses.add(ipAddress);
+ }
+
+ private void clearExcludedIpAddresses() {
+ if (!mFeatureFlags.epdgSelectionExcludeFailedIpAddress()) {
+ return;
+ }
+ Log.d(TAG, "Cleared temporary excluded addresses");
+ mTemporaryExcludedAddresses.clear();
+ }
+
+ private boolean isInExcludedIpAddresses(InetAddress ipAddress) {
+ return mTemporaryExcludedAddresses.contains(ipAddress);
+ }
+
private CompletableFuture<Map.Entry<String, List<InetAddress>>> submitDnsResolverQuery(
String domainName, Network network, int queryType, Executor executor) {
CompletableFuture<Map.Entry<String, List<InetAddress>>> result = new CompletableFuture();
@@ -326,6 +371,36 @@ public class EpdgSelector {
Log.d(TAG, domain + ": " + domainNameToIpAddresses.get(domain));
}
}
+
+ private List<InetAddress> filterExcludedAddresses(List<InetAddress> ipList) {
+ if (!mFeatureFlags.epdgSelectionExcludeFailedIpAddress()) {
+ return ipList;
+ }
+ if (mTemporaryExcludedAddresses.containsAll(ipList)) {
+ Log.d(
+ TAG,
+ "All valid ip are tried and excluded, clear all"
+ + " excluded address and retry entire list again");
+ clearExcludedIpAddresses();
+ }
+
+ var filteredIpList =
+ ipList.stream().filter(ipAddress -> !isInExcludedIpAddresses(ipAddress)).toList();
+
+ int excludedIpNum = filteredIpList.size() - ipList.size();
+ if (excludedIpNum > 0) {
+ Log.d(
+ TAG,
+ "Excluded "
+ + excludedIpNum
+ + " out of "
+ + ipList.size()
+ + " addresses from the list due to recent failures");
+ }
+
+ return filteredIpList;
+ }
+
/**
* Returns a list of unique IP addresses corresponding to the given domain names, in the same
* order of the input. Runs DNS resolution across parallel threads.
@@ -562,19 +637,19 @@ public class EpdgSelector {
return resultIpList;
}
- private void prioritizeIp(@NonNull List<InetAddress> validIpList, @EpdgAddressOrder int order) {
- switch (order) {
- case IPV4_PREFERRED:
- validIpList.sort(inetAddressComparator);
- break;
- case IPV6_PREFERRED:
- validIpList.sort(inetAddressComparator.reversed());
- break;
- case SYSTEM_PREFERRED:
- break;
- default:
+ private List<InetAddress> prioritizeIp(
+ @NonNull List<InetAddress> validIpList, @EpdgAddressOrder int order) {
+ return switch (order) {
+ case IPV4_PREFERRED -> validIpList.stream().sorted(inetAddressComparator).toList();
+ case IPV6_PREFERRED -> validIpList.stream()
+ .sorted(inetAddressComparator.reversed())
+ .toList();
+ case SYSTEM_PREFERRED -> validIpList;
+ default -> {
Log.w(TAG, "Invalid EpdgAddressOrder : " + order);
- }
+ yield validIpList;
+ }
+ };
}
private String[] splitMccMnc(String plmn) {
@@ -1290,9 +1365,10 @@ public class EpdgSelector {
}
if (!validIpList.isEmpty()) {
- prioritizeIp(validIpList, order);
- selectorCallback.onServerListChanged(
- transactionId, removeDuplicateIp(validIpList));
+ validIpList = removeDuplicateIp(validIpList);
+ validIpList = filterExcludedAddresses(validIpList);
+ validIpList = prioritizeIp(validIpList, order);
+ selectorCallback.onServerListChanged(transactionId, validIpList);
} else {
selectorCallback.onError(
transactionId,
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index aa2b0da..fc9be74 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -18,6 +18,7 @@ package com.google.android.iwlan.epdg;
import static android.net.ipsec.ike.ike3gpp.Ike3gppData.DATA_TYPE_NOTIFY_BACKOFF_TIMER;
import static android.net.ipsec.ike.ike3gpp.Ike3gppData.DATA_TYPE_NOTIFY_N1_MODE_INFORMATION;
+import static android.net.ipsec.ike.ike3gpp.Ike3gppParams.PDU_SESSION_ID_UNSET;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@@ -62,6 +63,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.CarrierConfigManager;
@@ -236,12 +238,34 @@ public class EpdgTunnelManager {
SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512);
}
- private static final int PDU_SESSION_ID_UNSET = 0;
-
// TODO(b/239753287): Some networks request DEVICE_IDENTITY, but errors out when parsing
// the response. Temporarily disabled.
private static final boolean INCLUDE_DEVICE_IDENTITY = false;
+ public static final int BRINGDOWN_REASON_UNKNOWN = 0;
+ public static final int BRINGDOWN_REASON_DISABLE_N1_MODE = 1;
+ public static final int BRINGDOWN_REASON_ENABLE_N1_MODE = 2;
+
+ @IntDef({
+ BRINGDOWN_REASON_UNKNOWN,
+ BRINGDOWN_REASON_DISABLE_N1_MODE,
+ BRINGDOWN_REASON_ENABLE_N1_MODE,
+ })
+ public @interface TunnelBringDownReason {}
+
+ private static String bringdownReasonToString(@TunnelBringDownReason int reason) {
+ switch (reason) {
+ case BRINGDOWN_REASON_UNKNOWN:
+ return "BRINGDOWN_REASON_UNKNOWN";
+ case BRINGDOWN_REASON_DISABLE_N1_MODE:
+ return "BRINGDOWN_REASON_DISABLE_N1_MODE";
+ case BRINGDOWN_REASON_ENABLE_N1_MODE:
+ return "BRINGDOWN_REASON_ENABLE_N1_MODE";
+ default:
+ return "Unknown(" + reason + ")";
+ }
+ }
+
private final EpdgSelector.EpdgSelectorCallback mSelectorCallback =
new EpdgSelector.EpdgSelectorCallback() {
@Override
@@ -682,16 +706,37 @@ public class EpdgTunnelManager {
* @param tunnelCallback Used if no current or pending IWLAN tunnel exists
* @param iwlanTunnelMetrics Used to report metrics if no current or pending IWLAN tunnel exists
*/
+ // TODO(b/309866889): Clarify tunnel bring down reason for tunnel closure.
public void closeTunnel(
@NonNull String apnName,
boolean forceClose,
@NonNull TunnelCallback tunnelCallback,
@NonNull IwlanTunnelMetricsImpl iwlanTunnelMetrics) {
+ closeTunnel(
+ apnName, forceClose, tunnelCallback, iwlanTunnelMetrics, BRINGDOWN_REASON_UNKNOWN);
+ }
+
+ /**
+ * Closes tunnel for an apn with reason.
+ *
+ * @param apnName APN name
+ * @param forceClose if {@code true}, triggers a local cleanup of the tunnel; if {@code false},
+ * performs a normal closure procedure
+ * @param tunnelCallback The tunnelCallback for tunnel to be closed
+ * @param iwlanTunnelMetrics The metrics to be reported
+ * @param reason The reason for tunnel to be closed
+ */
+ public void closeTunnel(
+ @NonNull String apnName,
+ boolean forceClose,
+ @NonNull TunnelCallback tunnelCallback,
+ @NonNull IwlanTunnelMetricsImpl iwlanTunnelMetrics,
+ @TunnelBringDownReason int reason) {
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_TUNNEL_BRINGDOWN_REQUEST,
new TunnelBringdownRequest(
- apnName, forceClose, tunnelCallback, iwlanTunnelMetrics)));
+ apnName, forceClose, tunnelCallback, iwlanTunnelMetrics, reason)));
}
/**
@@ -1128,9 +1173,9 @@ public class EpdgTunnelManager {
}
}
- if (isN1ModeSupported() && setupRequest.pduSessionId() != PDU_SESSION_ID_UNSET) {
- // Configures the PduSession ID in N1_MODE_CAPABILITY payload
- // to notify the server that UE supports N1_MODE
+ if (setupRequest.pduSessionId() != PDU_SESSION_ID_UNSET) {
+ // Includes N1_MODE_CAPABILITY NOTIFY payload in IKE_AUTH exchange when PDU session ID
+ // is set; otherwise, do not include.
builder3gppParams.setPduSessionId((byte) setupRequest.pduSessionId());
}
@@ -1753,6 +1798,7 @@ public class EpdgTunnelManager {
tunnelConfig.getTunnelCallback().onOpened(apnName, linkProperties);
reportIwlanError(apnName, new IwlanError(IwlanError.NO_ERROR));
+ getEpdgSelector().onEpdgConnectedSuccessfully();
mIkeTunnelEstablishmentDuration =
System.currentTimeMillis() - mIkeTunnelEstablishmentStartTime;
@@ -1822,6 +1868,8 @@ public class EpdgTunnelManager {
} else {
reportIwlanError(apnName, iwlanError);
}
+
+ getEpdgSelector().onEpdgConnectionFailed(mEpdgAddress);
}
Log.d(TAG, "Tunnel Closed: " + iwlanError);
@@ -1905,14 +1953,17 @@ public class EpdgTunnelManager {
TunnelBringdownRequest bringdownRequest = (TunnelBringdownRequest) msg.obj;
apnName = bringdownRequest.mApnName;
boolean forceClose = bringdownRequest.mForceClose;
+ int reason = bringdownRequest.mBringDownReason;
tunnelConfig = mApnNameToTunnelConfig.get(apnName);
if (tunnelConfig == null) {
Log.w(
TAG,
"Bringdown request: No tunnel exists for apn: "
+ apnName
- + "forced: "
- + forceClose);
+ + ", forced: "
+ + forceClose
+ + ", bringdown reason: "
+ + bringdownReasonToString(reason));
} else {
if (forceClose) {
tunnelConfig.getIkeSession().kill();
@@ -1920,9 +1971,17 @@ public class EpdgTunnelManager {
tunnelConfig.getIkeSession().close();
}
}
+ // TODO(b/309867892): Include tunnel bring down reason in metrics.
int numClosed = closePendingRequestsForApn(apnName);
if (numClosed > 0) {
- Log.d(TAG, "Closed " + numClosed + " pending requests for apn: " + apnName);
+ Log.d(
+ TAG,
+ "Closed "
+ + numClosed
+ + " pending requests for apn: "
+ + apnName
+ + ", bringdown reason: "
+ + bringdownReasonToString(reason));
}
if (tunnelConfig == null && numClosed == 0) {
// IwlanDataService expected to close a (pending or up) tunnel but was not
@@ -2181,6 +2240,17 @@ public class EpdgTunnelManager {
@VisibleForTesting
void validateAndSetEpdgAddress(List<InetAddress> selectorResultList) {
+ if (mFeatureFlags.epdgSelectionExcludeFailedIpAddress()) {
+ Log.d(
+ TAG,
+ "Selected first ePDG address "
+ + selectorResultList.get(0)
+ + " from available ePDG address list: "
+ + Arrays.toString(selectorResultList.toArray()));
+ mValidEpdgInfo.setAddrList(selectorResultList);
+ mEpdgAddress = selectorResultList.get(0);
+ return;
+ }
List<InetAddress> addrList = mValidEpdgInfo.getAddrList();
if (addrList == null || !addrList.equals(selectorResultList)) {
Log.d(TAG, "Update ePDG address list.");
@@ -2287,16 +2357,19 @@ public class EpdgTunnelManager {
final boolean mForceClose;
final TunnelCallback mTunnelCallback;
final IwlanTunnelMetricsImpl mIwlanTunnelMetrics;
+ final int mBringDownReason;
private TunnelBringdownRequest(
String apnName,
boolean forceClose,
TunnelCallback tunnelCallback,
- IwlanTunnelMetricsImpl iwlanTunnelMetrics) {
+ IwlanTunnelMetricsImpl iwlanTunnelMetrics,
+ @TunnelBringDownReason int reason) {
mApnName = apnName;
mForceClose = forceClose;
mTunnelCallback = tunnelCallback;
mIwlanTunnelMetrics = iwlanTunnelMetrics;
+ mBringDownReason = reason;
}
}
@@ -2569,15 +2642,6 @@ public class EpdgTunnelManager {
}
@VisibleForTesting
- boolean isN1ModeSupported() {
- int[] nrCarrierCaps =
- getConfig(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
- Log.d(TAG, "KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY : " + Arrays.toString(nrCarrierCaps));
- return Arrays.stream(nrCarrierCaps)
- .anyMatch(cap -> cap == CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA);
- }
-
- @VisibleForTesting
boolean isTunnelConfigContainExistApn(String apnName) {
return mApnNameToTunnelConfig.containsKey(apnName);
}
diff --git a/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java b/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java
index d1ed9dd..299ed71 100644
--- a/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java
+++ b/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java
@@ -16,7 +16,7 @@
package com.google.android.iwlan.epdg;
-import android.net.Network;
+import com.android.internal.annotations.VisibleForTesting;
import com.google.auto.value.AutoValue;
@@ -43,6 +43,11 @@ public abstract class TunnelSetupRequest {
abstract boolean requestPcscf();
+ @VisibleForTesting
+ public int getPduSessionId() {
+ return pduSessionId();
+ }
+
public static Builder builder() {
return new AutoValue_TunnelSetupRequest.Builder()
.setSrcIpv4Address(Optional.empty())
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index b829068..eae41bb 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -895,6 +895,65 @@ public class ErrorPolicyManagerTest {
}
@Test
+ public void testWifiApChangedUnthrottle() 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("6", "12", "24"))
+ .setUnthrottlingEvents(
+ List.of(
+ "WIFI_CALLING_DISABLE_EVENT",
+ "WIFI_DISABLE_EVENT",
+ "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ + "}, {"
+ + 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();
+ 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 = 6, 12, 24
+ IwlanError iwlanError = buildIwlanIkeAuthFailedError();
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(6, time);
+
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.WIFI_AP_CHANGED_EVENT)
+ .sendToTarget();
+ advanceClockByTimeMs(500);
+ verify(mMockDataServiceProvider, times(1)).notifyApnUnthrottled(eq(apn));
+
+ boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
+ assertTrue(bringUpTunnel);
+
+ iwlanError = buildIwlanIkeAuthFailedError();
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(6, time);
+ }
+
+ @Test
public void testGetDataFailCauseRetryTime() throws Exception {
String apn1 = "ims";
String apn2 = "mms";
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index c2b4e28..dc2b2cf 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -19,6 +19,11 @@ package com.google.android.iwlan;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.ipsec.ike.ike3gpp.Ike3gppParams.PDU_SESSION_ID_UNSET;
+import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
+import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_NR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -56,8 +61,10 @@ import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.vcn.VcnTransportInfo;
+import android.os.PersistableBundle;
import android.os.test.TestLooper;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.CarrierConfigManager;
import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -69,6 +76,7 @@ import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
import android.telephony.data.IDataServiceCallback;
import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
@@ -80,6 +88,7 @@ import com.google.android.iwlan.epdg.EpdgTunnelManager;
import com.google.android.iwlan.epdg.NetworkSliceSelectionAssistanceInformation;
import com.google.android.iwlan.epdg.TunnelLinkProperties;
import com.google.android.iwlan.epdg.TunnelSetupRequest;
+import com.google.android.iwlan.flags.FeatureFlags;
import com.google.android.iwlan.proto.MetricsAtom;
import org.junit.After;
@@ -133,6 +142,8 @@ public class IwlanDataServiceTest {
@Mock private LinkAddress mMockIPv6LinkAddress;
@Mock private Inet4Address mMockInet4Address;
@Mock private Inet6Address mMockInet6Address;
+ @Mock private CarrierConfigManager mMockCarrierConfigManager;
+ @Mock private FeatureFlags mFakeFeatureFlags;
MockitoSession mStaticMockSession;
@@ -249,7 +260,10 @@ public class IwlanDataServiceTest {
when(mMockIPv4LinkAddress.getAddress()).thenReturn(mMockInet4Address);
when(mMockIPv6LinkAddress.getAddress()).thenReturn(mMockInet6Address);
- mIwlanDataService = spy(new IwlanDataService());
+ mIwlanDataService = spy(new IwlanDataService(mFakeFeatureFlags));
+
+ when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
+ .thenReturn(mMockCarrierConfigManager);
// Injects the test looper into the IwlanDataServiceHandler
doReturn(mTestLooper.getLooper()).when(mIwlanDataService).getLooper();
mIwlanDataService.setAppContext(mMockContext);
@@ -434,7 +448,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false /* isHandover */,
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
LinkProperties newLinkProperties = new LinkProperties(mLinkProperties);
newLinkProperties.setInterfaceName("wlan0");
@@ -600,7 +615,8 @@ public class IwlanDataServiceTest {
mLinkProperties,
false, /* isHandover */
1, /* pduSessionId */
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
mTestLooper.dispatchAll();
@@ -751,6 +767,33 @@ public class IwlanDataServiceTest {
}
@Test
+ public void testIwlanSetupDataCallWithBringUpTunnelAndNullApnSetting() {
+ DataProfile dp = buildImsDataProfileWithEmptyApnSetting();
+
+ /* Wifi is connected */
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
+
+ mSpyIwlanDataServiceProvider.setupDataCall(
+ AccessNetworkType.IWLAN, /* AccessNetworkType */
+ dp, /* dataProfile */
+ false, /* isRoaming */
+ true, /* allowRoaming */
+ DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */
+ null, /* LinkProperties */
+ 1, /* pduSessionId */
+ null, /* sliceInfo */
+ null, /* trafficDescriptor */
+ true, /* matchAllRuleAllowed */
+ mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
+
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_ERROR_INVALID_ARG), isNull());
+ }
+
+ @Test
public void testSliceInfoInclusionInDataCallResponse() throws Exception {
DataProfile dp = buildImsDataProfile();
@@ -810,7 +853,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false, /* isHandover */
1, /* pduSessionId */
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
@@ -847,7 +891,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false, /* isHandover */
1, /* pduSessionId */
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
@@ -891,7 +936,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false, /* isHandover */
1, /* pduSessionId */
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
@@ -949,7 +995,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false, /* isHandover */
1, /* pduSessionId */
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1014,7 +1061,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1066,7 +1114,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1117,7 +1166,7 @@ public class IwlanDataServiceTest {
.obtainMessage(
IwlanEventListener.CALL_STATE_CHANGED_EVENT,
DEFAULT_SLOT_INDEX,
- TelephonyManager.CALL_STATE_IDLE)
+ CALL_STATE_IDLE)
.sendToTarget();
mSpyIwlanDataServiceProvider.setTunnelState(
@@ -1127,7 +1176,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1175,7 +1225,7 @@ public class IwlanDataServiceTest {
.obtainMessage(
IwlanEventListener.CALL_STATE_CHANGED_EVENT,
DEFAULT_SLOT_INDEX,
- TelephonyManager.CALL_STATE_IDLE)
+ CALL_STATE_IDLE)
.sendToTarget();
mSpyIwlanDataServiceProvider.setTunnelState(
@@ -1185,7 +1235,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1243,7 +1294,8 @@ public class IwlanDataServiceTest {
null /* linkProperties */,
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1301,7 +1353,8 @@ public class IwlanDataServiceTest {
null /* linkProperties */,
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1394,6 +1447,16 @@ public class IwlanDataServiceTest {
mTestLooper.dispatchAll();
}
+ private DataProfile buildImsDataProfileWithEmptyApnSetting() {
+ return new DataProfile.Builder()
+ .setTrafficDescriptor(
+ new TrafficDescriptor.Builder().setDataNetworkName("").build())
+ .setType(1)
+ .enable(true)
+ .setPreferred(true)
+ .build();
+ }
+
private DataProfile buildImsDataProfile() {
return buildDataProfile(ApnSetting.TYPE_IMS);
}
@@ -1653,7 +1716,8 @@ public class IwlanDataServiceTest {
null /* linkProperties */,
false /* isHandover */,
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1671,7 +1735,7 @@ public class IwlanDataServiceTest {
.thenReturn(DataFailCause.ERROR_UNSPECIFIED);
// Simulate IwlanDataService.onUnbind() which force close all tunnels
- mSpyIwlanDataServiceProvider.forceCloseTunnels();
+ mSpyIwlanDataServiceProvider.forceCloseTunnels(EpdgTunnelManager.BRINGDOWN_REASON_UNKNOWN);
// Simulate DataService.onUnbind() which remove all IwlanDataServiceProviders
mSpyIwlanDataServiceProvider.close();
mTestLooper.dispatchAll();
@@ -1681,7 +1745,8 @@ public class IwlanDataServiceTest {
eq(TEST_APN_NAME),
eq(true),
any(IwlanTunnelCallback.class),
- any(IwlanTunnelMetricsImpl.class));
+ any(IwlanTunnelMetricsImpl.class),
+ eq(EpdgTunnelManager.BRINGDOWN_REASON_UNKNOWN));
assertNotNull(mIwlanDataService.mIwlanDataServiceHandler);
// Should not raise NullPointerException
mSpyIwlanDataServiceProvider
@@ -1707,7 +1772,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false /* isHandover */,
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1774,7 +1840,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false /* isHandover */,
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -1817,7 +1884,8 @@ public class IwlanDataServiceTest {
null, /* linkProperties */
false /* isHandover */,
1 /* pduSessionId */,
- true /* isImsOrEmergency */);
+ true /* isImsOrEmergency */,
+ true /* isDataCallSetupWithN1 */);
mSpyIwlanDataServiceProvider.setMetricsAtom(
TEST_APN_NAME,
@@ -2003,4 +2071,249 @@ public class IwlanDataServiceTest {
.setSliceInfo(SLICE_INFO)
.build();
}
+
+ private void setupMockForGetConfig(PersistableBundle bundle) {
+ if (bundle == null) {
+ bundle = new PersistableBundle();
+ }
+ when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
+ .thenReturn(mMockCarrierConfigManager);
+ when(mMockCarrierConfigManager.getConfigForSubId(DEFAULT_SLOT_INDEX)).thenReturn(bundle);
+ }
+
+ private void mockCarrierConfigForN1Mode(boolean supportN1Mode) {
+ PersistableBundle bundle = new PersistableBundle();
+ if (supportN1Mode) {
+ bundle.putIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ new int[] {
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA
+ });
+ } else {
+ bundle.putIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ new int[] {CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA});
+ }
+ setupMockForGetConfig(bundle);
+ }
+
+ private void mockCallState(int callState) {
+ onSystemDefaultNetworkConnected(TRANSPORT_CELLULAR);
+
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT, DEFAULT_SLOT_INDEX, callState)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME, 64, true, TelephonyManager.NETWORK_TYPE_LTE, false, true, 1);
+ }
+
+ private void updatePreferredNetworkType(long networkTypeBitmask) {
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ 0 /* unused */,
+ networkTypeBitmask)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIsN1ModeSupported() {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ new int[] {
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA
+ });
+ setupMockForGetConfig(bundle);
+ assertTrue(mSpyIwlanDataServiceProvider.isN1ModeSupported());
+
+ bundle.putIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ new int[] {
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
+ });
+ setupMockForGetConfig(bundle);
+ assertFalse(mSpyIwlanDataServiceProvider.isN1ModeSupported());
+ }
+
+ @Test
+ public void testMultipleAllowedNetworkTypeChangeInIdle_updateN1Mode() throws Exception {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(true);
+ mockCallState(CALL_STATE_IDLE);
+ mockSetupDataCallWithPduSessionId(0);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+
+ /* Check closeTunnel() is called. */
+ verify(mMockEpdgTunnelManager, atLeastOnce())
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(true),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class),
+ eq(EpdgTunnelManager.BRINGDOWN_REASON_ENABLE_N1_MODE));
+
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_LTE);
+
+ verify(mMockEpdgTunnelManager, atLeastOnce())
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(true),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class),
+ eq(EpdgTunnelManager.BRINGDOWN_REASON_DISABLE_N1_MODE));
+ }
+
+ @Test
+ public void testMultipleAllowedNetworkTypeChangeInCall_preferenceChanged_updateAfterCallEnds()
+ throws Exception {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(true);
+ mockCallState(CALL_STATE_RINGING);
+ mockSetupDataCallWithPduSessionId(0);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_LTE);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+
+ verify(mMockEpdgTunnelManager, never())
+ .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
+
+ // in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ CALL_STATE_IDLE)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgTunnelManager, atLeastOnce())
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(true),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class),
+ eq(EpdgTunnelManager.BRINGDOWN_REASON_ENABLE_N1_MODE));
+ }
+
+ @Test
+ public void testMultipleAllowedNetworkTypeChangeInCall_preferenceNotChanged_noUpdate()
+ throws Exception {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(true);
+ mockCallState(CALL_STATE_RINGING);
+ mockSetupDataCallWithPduSessionId(0);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_LTE);
+
+ verify(mMockEpdgTunnelManager, never())
+ .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
+
+ // in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ CALL_STATE_IDLE)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgTunnelManager, never())
+ .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
+ }
+
+ @Test
+ public void testOnAllowedNetworkTypeChange_flagDisabled_noTunnelClose() {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(false);
+ mockCarrierConfigForN1Mode(true);
+ mockCallState(CALL_STATE_IDLE);
+ mockSetupDataCallWithPduSessionId(0);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+
+ verify(mMockEpdgTunnelManager, never())
+ .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
+ }
+
+ @Test
+ public void testOnAllowedNetworkTypeChange_n1ModeNotSupported_noTunnelClose() {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(false);
+ mockCallState(CALL_STATE_IDLE);
+ mockSetupDataCallWithPduSessionId(0);
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+
+ verify(mMockEpdgTunnelManager, never())
+ .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
+ }
+
+ @Test
+ public void testN1ModeNotSupported_tunnelBringupWithNoN1ModeCapability() {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(false);
+ mockSetupDataCallWithPduSessionId(1);
+
+ ArgumentCaptor<TunnelSetupRequest> tunnelSetupRequestCaptor =
+ ArgumentCaptor.forClass(TunnelSetupRequest.class);
+ verify(mMockEpdgTunnelManager, times(1))
+ .bringUpTunnel(tunnelSetupRequestCaptor.capture(), any(), any());
+ TunnelSetupRequest tunnelSetupRequest = tunnelSetupRequestCaptor.getValue();
+ assertEquals(PDU_SESSION_ID_UNSET, tunnelSetupRequest.getPduSessionId());
+ }
+
+ @Test
+ public void testNoN1ModeCapabilityInOngoingDataCall_newTunnelBringup_doNotIncludeN1() {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+ mockCarrierConfigForN1Mode(true);
+ mockSetupDataCallWithPduSessionId(0);
+
+ ArgumentCaptor<TunnelSetupRequest> tunnelSetupRequestCaptor =
+ ArgumentCaptor.forClass(TunnelSetupRequest.class);
+ verify(mMockEpdgTunnelManager, times(1))
+ .bringUpTunnel(tunnelSetupRequestCaptor.capture(), any(), any());
+ TunnelSetupRequest tunnelSetupRequest = tunnelSetupRequestCaptor.getValue();
+ assertEquals(PDU_SESSION_ID_UNSET, tunnelSetupRequest.getPduSessionId());
+
+ updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
+ mockSetupDataCallWithPduSessionId(1);
+
+ verify(mMockEpdgTunnelManager, times(1))
+ .bringUpTunnel(tunnelSetupRequestCaptor.capture(), any(), any());
+ tunnelSetupRequest = tunnelSetupRequestCaptor.getValue();
+ assertEquals(PDU_SESSION_ID_UNSET, tunnelSetupRequest.getPduSessionId());
+ }
+
+ private void mockSetupDataCallWithPduSessionId(int pduSessionId) {
+ DataProfile dp = buildImsDataProfile();
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
+ mSpyIwlanDataServiceProvider.setupDataCall(
+ AccessNetworkType.IWLAN, /* AccessNetworkType */
+ dp, /* dataProfile */
+ false, /* isRoaming */
+ false, /* allowRoaming */
+ DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */
+ null, /* LinkProperties */
+ pduSessionId, /* pduSessionId */
+ null, /* sliceInfo */
+ null, /* trafficDescriptor */
+ true, /* matchAllRuleAllowed */
+ mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgTunnelManager, times(1))
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
+ }
}
diff --git a/test/com/google/android/iwlan/IwlanEventListenerTest.java b/test/com/google/android/iwlan/IwlanEventListenerTest.java
index 1552c97..a922a10 100644
--- a/test/com/google/android/iwlan/IwlanEventListenerTest.java
+++ b/test/com/google/android/iwlan/IwlanEventListenerTest.java
@@ -18,8 +18,17 @@ package com.google.android.iwlan;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static org.mockito.Mockito.*;
-
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -35,6 +44,8 @@ import android.telephony.TelephonyManager;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
+import com.google.android.iwlan.flags.FeatureFlags;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -42,7 +53,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
public class IwlanEventListenerTest {
private static final String TAG = "IwlanEventListenerTest";
@@ -59,6 +71,7 @@ public class IwlanEventListenerTest {
@Mock private ImsManager mMockImsManager;
@Mock private ImsMmTelManager mMockImsMmTelManager;
@Mock private TelephonyManager mMockTelephonyManager;
+ @Mock private FeatureFlags mFakeFeatureFlags;
private static final int DEFAULT_SLOT_INDEX = 0;
private static final int OTHER_SLOT_INDEX = 1;
@@ -70,6 +83,7 @@ public class IwlanEventListenerTest {
Uri.parse("content://telephony/siminfo/cross_sim_calling_enabled/2");
private static final Uri WFC_ENABLED_URI = Uri.parse("content://telephony/siminfo/wfc/2");
private IwlanEventListener mIwlanEventListener;
+ private IwlanEventListener mSpyIwlanEventListener;
private List<Integer> events;
MockitoSession mStaticMockSession;
@@ -111,6 +125,8 @@ public class IwlanEventListenerTest {
IwlanEventListener.resetAllInstances();
mIwlanEventListener = IwlanEventListener.getInstance(mMockContext, DEFAULT_SLOT_INDEX);
+ mSpyIwlanEventListener =
+ spy(new IwlanEventListener(mMockContext, DEFAULT_SLOT_INDEX, mFakeFeatureFlags));
}
@After
@@ -348,4 +364,54 @@ public class IwlanEventListenerTest {
mIwlanEventListener.notifyCurrentSetting(WFC_ENABLED_URI);
verify(mMockMessage, times(1)).sendToTarget();
}
+
+ @SuppressLint("MissingPermission")
+ @Test
+ public void testDisable5gViaUi() throws Exception {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt(),
+ eq(TelephonyManager.NETWORK_TYPE_BITMASK_LTE)))
+ .thenReturn(mMockMessage);
+
+ events = new ArrayList<>();
+ events.add(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT);
+ mIwlanEventListener.addEventListener(events, mMockHandler);
+ mSpyIwlanEventListener.registerTelephonyCallback();
+ TelephonyCallback.AllowedNetworkTypesListener mTelephonyCallback =
+ mSpyIwlanEventListener.getTelephonyCallback();
+
+ mTelephonyCallback.onAllowedNetworkTypesChanged(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+ TelephonyManager.NETWORK_TYPE_BITMASK_LTE);
+ verify(mMockMessage, times(1)).sendToTarget();
+ }
+
+ @SuppressLint("MissingPermission")
+ @Test
+ public void testEnable5gViaUi() throws Exception {
+ when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
+
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt(),
+ eq(TelephonyManager.NETWORK_TYPE_BITMASK_NR)))
+ .thenReturn(mMockMessage);
+
+ events = new ArrayList<>();
+ events.add(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT);
+ mIwlanEventListener.addEventListener(events, mMockHandler);
+ mSpyIwlanEventListener.registerTelephonyCallback();
+ TelephonyCallback.AllowedNetworkTypesListener mTelephonyCallback =
+ mSpyIwlanEventListener.getTelephonyCallback();
+
+ mTelephonyCallback.onAllowedNetworkTypesChanged(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+ TelephonyManager.NETWORK_TYPE_BITMASK_NR);
+ verify(mMockMessage, times(1)).sendToTarget();
+ }
}
diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
index 9727893..d328816 100644
--- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
@@ -52,6 +52,7 @@ import android.util.Log;
import com.google.android.iwlan.ErrorPolicyManager;
import com.google.android.iwlan.IwlanError;
+import com.google.android.iwlan.flags.FeatureFlags;
import org.junit.After;
import org.junit.Before;
@@ -70,8 +71,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
-import com.google.android.iwlan.flags.FeatureFlags;
-
public class EpdgSelectorTest {
private static final String TAG = "EpdgSelectorTest";
@@ -747,6 +746,181 @@ public class EpdgSelectorTest {
assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2));
}
+ @Test
+ public void testTemporaryExcludedIpAddressWhenDisabledExcludeFailedIp() throws Exception {
+ doReturn(false).when(mfakeFeatureFlags).epdgSelectionExcludeFailedIpAddress();
+ when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver);
+ doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork);
+ doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork);
+
+ String fqdnFromRplmn = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org";
+ final String staticAddr = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org";
+
+ when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300122");
+ mTestBundle.putStringArray(
+ CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, new String[] {"300-122"});
+
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC
+ });
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN});
+
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, staticAddr);
+
+ mFakeDns.setAnswer(fqdnFromRplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+ mFakeDns.setAnswer(staticAddr, new String[] {TEST_IP_ADDRESS_1, TEST_IPV6_ADDRESS}, TYPE_A);
+
+ ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS));
+ // Flag disabled should not affect the result
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectedSuccessfully();
+ // Flag disabled should not affect the result
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+ }
+
+ @Test
+ public void testTemporaryExcludedIpAddressWhenEnabledExcludeFailedIp() throws Exception {
+ doReturn(true).when(mfakeFeatureFlags).epdgSelectionExcludeFailedIpAddress();
+ when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver);
+ doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork);
+ doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork);
+
+ final String fqdnFromRplmn = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org";
+ final String staticAddr = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org";
+
+ when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300122");
+ mTestBundle.putStringArray(
+ CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, new String[] {"300-122"});
+
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC
+ });
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN});
+
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, staticAddr);
+
+ mFakeDns.setAnswer(fqdnFromRplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+ mFakeDns.setAnswer(staticAddr, new String[] {TEST_IP_ADDRESS_1, TEST_IPV6_ADDRESS}, TYPE_A);
+
+ ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS));
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ // Reset temporary excluded ip addresses
+ mEpdgSelector.onEpdgConnectedSuccessfully();
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS));
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IPV6_ADDRESS));
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(InetAddress.getByName(TEST_IP_ADDRESS_1)).toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS_1));
+ // All ip addresses removed, should reset excluded address
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IP_ADDRESS_1),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS_1));
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS),
+ InetAddress.getByName(TEST_IPV6_ADDRESS))
+ .toArray(),
+ testInetAddresses.toArray());
+
+ // When the original result changed
+ mFakeDns.setAnswer(staticAddr, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(fqdnFromRplmn, new String[] {TEST_IP_ADDRESS_3}, TYPE_A);
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(InetAddress.getByName(TEST_IP_ADDRESS_3)).toArray(),
+ testInetAddresses.toArray());
+
+ mEpdgSelector.onEpdgConnectionFailed(InetAddress.getByName(TEST_IP_ADDRESS_3));
+ // It should also reset the excluded list once all ip addresses are excluded
+ testInetAddresses = getValidatedServerListWithDefaultParams(false);
+ assertArrayEquals(
+ List.of(
+ InetAddress.getByName(TEST_IP_ADDRESS_3),
+ InetAddress.getByName(TEST_IP_ADDRESS_1))
+ .toArray(),
+ testInetAddresses.toArray());
+ }
+
private void setAnswerForCellularMethod(boolean isEmergency, int mcc, int mnc)
throws Exception {
String expectedFqdn1 =
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index de53193..ff413b5 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -176,6 +176,7 @@ public class EpdgTunnelManagerTest {
public void setUp() throws Exception {
EpdgTunnelManager.resetAllInstances();
when(mMockContext.getSystemService(eq(IpSecManager.class))).thenReturn(mMockIpSecManager);
+ when(mFakeFeatureFlags.epdgSelectionExcludeFailedIpAddress()).thenReturn(false);
mEpdgTunnelManager =
spy(new EpdgTunnelManager(mMockContext, DEFAULT_SLOT_INDEX, mFakeFeatureFlags));
@@ -1083,6 +1084,59 @@ public class EpdgTunnelManagerTest {
verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
}
+ @Test
+ public void testGetValidEpdgAddress_WhenExcludeFailedIpEnabled() throws Exception {
+ String testApnName = "www.xyz.com";
+ when(mFakeFeatureFlags.epdgSelectionExcludeFailedIpAddress()).thenReturn(true);
+
+ List<InetAddress> ipList1 =
+ List.of(InetAddress.getByName("1.1.1.1"), InetAddress.getByName("8.8.8.8"));
+ mEpdgTunnelManager.validateAndSetEpdgAddress(ipList1);
+
+ IwlanError error = new IwlanError(new IkeInternalException(new IOException()));
+
+ doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
+ setupMockForGetConfig(null);
+
+ doReturn(null)
+ .doReturn(null)
+ .when(mMockIkeSessionCreator)
+ .createIkeSession(
+ eq(mMockContext),
+ any(IkeSessionParams.class),
+ any(ChildSessionParams.class),
+ any(Executor.class),
+ any(IkeSessionCallback.class),
+ any(ChildSessionCallback.class));
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName));
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ List<InetAddress> ipList2 =
+ List.of(InetAddress.getByName("1.1.1.1"), InetAddress.getByName("8.8.8.8"));
+ mEpdgTunnelManager.sendSelectionRequestComplete(
+ ipList2, new IwlanError(IwlanError.NO_ERROR), 1);
+ mTestLooper.dispatchAll();
+
+ // When exclude failed IP is enabled, EpdgSelector is responsible to excluding the failed
+ // IP address from result. EpdgTunnelManager should always use the first IP address from
+ // the ePDG selection result IP address list, regardless the list is same as prev or not
+ EpdgTunnelManager.TmIkeSessionCallback ikeSessionCallback =
+ verifyCreateIkeSession(ipList2.get(0));
+ ikeSessionCallback.onClosedWithException(
+ new IkeInternalException(new IOException("Retransmitting failure")));
+ mTestLooper.dispatchAll();
+
+ verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error));
+ verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
+ }
+
private EpdgTunnelManager.TmIkeSessionCallback verifyCreateIkeSession(InetAddress ip)
throws Exception {
ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
@@ -1506,6 +1560,8 @@ public class EpdgTunnelManagerTest {
ChildSessionCallback childSessionCallback =
ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
verifyTunnelOnOpened(toBeOpenedApnName, childSessionCallback);
+ verify(mMockEpdgSelector, times(0)).onEpdgConnectionFailed(any());
+ verify(mMockEpdgSelector).onEpdgConnectedSuccessfully();
}
@Test
@@ -1811,23 +1867,13 @@ public class EpdgTunnelManagerTest {
}
@Test
- public void testEnableN1modeCapabilityWithValidPduSessionId_nonInclusion() throws Exception {
- verifyN1modeCapability(0, false);
- }
-
- @Test
- public void testDisableN1modeCapabilityWithInvalidPduSessionId_nonInclusion() throws Exception {
- verifyN1modeCapability(0, false);
+ public void testUnsetPduSessionIdInclusion() throws Exception {
+ verifyN1modeCapability(0);
}
@Test
- public void testEnableN1modeCapabilityWithValidPduSessionId_inclusion() throws Exception {
- verifyN1modeCapability(8, true);
- }
-
- @Test
- public void testDisableN1modeCapabilityWithValidPduSessionId_nonInclusion() throws Exception {
- verifyN1modeCapability(8, false);
+ public void testPduSessionIdInclusion() throws Exception {
+ verifyN1modeCapability(8);
}
@Test
@@ -1936,9 +1982,7 @@ public class EpdgTunnelManagerTest {
verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
}
- private void verifyN1modeCapability(int pduSessionId, boolean isN1ModeSupported)
- throws Exception {
- doReturn(isN1ModeSupported).when(mEpdgTunnelManager).isN1ModeSupported();
+ private void verifyN1modeCapability(int pduSessionId) throws Exception {
String testApnName = "www.xyz.com";
byte pduSessionIdToByte = (byte) pduSessionId;
@@ -1992,11 +2036,7 @@ public class EpdgTunnelManagerTest {
byte pduSessionIdByte =
ikeSessionParams.getIke3gppExtension().getIke3gppParams().getPduSessionId();
- if (isN1ModeSupported && pduSessionId != 0) {
- assertEquals(pduSessionIdToByte, pduSessionIdByte);
- } else {
- assertEquals(0, pduSessionIdByte);
- }
+ assertEquals(pduSessionIdToByte, pduSessionIdByte);
}
@Test
@@ -2526,6 +2566,8 @@ public class EpdgTunnelManagerTest {
mTestLooper.dispatchAll();
verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error));
+ verify(mMockEpdgSelector).onEpdgConnectionFailed(eq(EXPECTED_EPDG_ADDRESSES.get(0)));
+ verify(mMockEpdgSelector, times(0)).onEpdgConnectedSuccessfully();
verify(mMockIwlanTunnelCallback, atLeastOnce()).onClosed(eq(testApnName), eq(error));
}
@@ -2574,33 +2616,6 @@ public class EpdgTunnelManagerTest {
verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error));
}
- private boolean verifyIsN1ModeSupported(int[] nrAvailability) {
- PersistableBundle bundle = new PersistableBundle();
- bundle.putIntArray(
- CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, nrAvailability);
-
- setupMockForGetConfig(bundle);
-
- return mEpdgTunnelManager.isN1ModeSupported();
- }
-
- @Test
- public void testIsN1ModeSupportedTrue() throws Exception {
- assertTrue(
- verifyIsN1ModeSupported(
- new int[] {
- CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
- CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA
- }));
- }
-
- @Test
- public void testIsN1ModeSupportedFalse() throws Exception {
- assertFalse(
- verifyIsN1ModeSupported(
- new int[] {CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA}));
- }
-
@Test
public void testUpdateNetworkToOpenedTunnel() throws Exception {
String apnName = "ims";