diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-26 10:25:26 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-26 10:25:26 +0000 |
commit | 80c5091aae7eeb09c612befa55f20270131e69ea (patch) | |
tree | 01f3bf91a28d0e93be016189e68f497e78f6171b | |
parent | 7d7dc36d06218175626ede05e82da67cf669173e (diff) | |
parent | 260b35cd35a643a9de93747c6e5429f57a607d03 (diff) | |
download | Iwlan-80c5091aae7eeb09c612befa55f20270131e69ea.tar.gz |
Snap for 10209341 from 260b35cd35a643a9de93747c6e5429f57a607d03 to mainline-healthfitness-release
Change-Id: I0518ca4aae9104890b8138fdebaa07c63ba1c0f1
-rw-r--r-- | AndroidManifest.xml | 1 | ||||
-rw-r--r-- | assets/defaultiwlanerrorconfig.json | 15 | ||||
-rw-r--r-- | com.google.android.iwlan.xml | 1 | ||||
-rw-r--r-- | src/com/google/android/iwlan/ErrorPolicyManager.java | 18 | ||||
-rw-r--r-- | src/com/google/android/iwlan/IwlanDataService.java | 249 | ||||
-rw-r--r-- | src/com/google/android/iwlan/IwlanHelper.java | 42 | ||||
-rw-r--r-- | src/com/google/android/iwlan/IwlanNetworkService.java | 45 | ||||
-rw-r--r-- | src/com/google/android/iwlan/epdg/EpdgSelector.java | 213 | ||||
-rw-r--r-- | src/com/google/android/iwlan/epdg/EpdgTunnelManager.java | 311 | ||||
-rw-r--r-- | src/com/google/android/iwlan/epdg/SrvDnsResolver.java | 3 | ||||
-rw-r--r-- | src/com/google/android/iwlan/epdg/TunnelSetupRequest.java | 4 | ||||
-rw-r--r-- | src/com/google/android/iwlan/proto/MetricsAtom.java | 40 | ||||
-rw-r--r-- | test/com/google/android/iwlan/ErrorPolicyManagerTest.java | 28 | ||||
-rw-r--r-- | test/com/google/android/iwlan/IwlanDataServiceTest.java | 477 | ||||
-rw-r--r-- | test/com/google/android/iwlan/epdg/EpdgSelectorTest.java | 409 | ||||
-rw-r--r-- | test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java | 409 |
16 files changed, 1557 insertions, 708 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d4f541f..e0b0b3a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" coreApp="true" + android:sharedUserId="android.uid.system" package="com.google.android.iwlan"> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> diff --git a/assets/defaultiwlanerrorconfig.json b/assets/defaultiwlanerrorconfig.json index 212f611..2d2be95 100644 --- a/assets/defaultiwlanerrorconfig.json +++ b/assets/defaultiwlanerrorconfig.json @@ -68,20 +68,27 @@ { "ErrorType": "*", "ErrorDetails": ["*"], - "RetryArray": ["5", "10", "-1"], + "RetryArray": ["1", "2", "2", "10", "20", "40", "80", "160", "320", "640", "1280", "1800", "3600", "-1"], "UnthrottlingEvents": ["APM_ENABLE_EVENT", "APM_DISABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_AP_CHANGED_EVENT"] }, { "ErrorType": "GENERIC_ERROR_TYPE", "ErrorDetails": ["IO_EXCEPTION"], - "RetryArray": ["0", "0", "0", "60+r15", "120", "-1"], + "RetryArray": ["0", "0", "0", "30", "60+r15", "120", "-1"], "UnthrottlingEvents": ["APM_ENABLE_EVENT", "APM_DISABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_AP_CHANGED_EVENT"] }, { "ErrorType": "IKE_PROTOCOL_ERROR_TYPE", - "ErrorDetails": ["24", "9002"], - "RetryArray": ["10", "20", "40", "80", "160"], + "ErrorDetails": ["*"], + "RetryArray": ["5", "10", "10", "20", "40", "80", "160", "320", "640", "1280", "1800", "3600", "-1"], "UnthrottlingEvents": ["APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_CALLING_DISABLE_EVENT"] + }, + { + "ErrorType": "IKE_PROTOCOL_ERROR_TYPE", + "ErrorDetails": ["36"], + "RetryArray": ["0", "0", "0", "10", "20", "40", "80", "160", "320", "640", "1280", "1800", "3600", "-1"], + "UnthrottlingEvents": ["APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_CALLING_DISABLE_EVENT"], + "HandoverAttemptCount": 3 } ] } diff --git a/com.google.android.iwlan.xml b/com.google.android.iwlan.xml index f37b7c8..f91bb8a 100644 --- a/com.google.android.iwlan.xml +++ b/com.google.android.iwlan.xml @@ -3,5 +3,6 @@ <privapp-permissions package="com.google.android.iwlan"> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <permission name="android.permission.SCHEDULE_EXACT_ALARM"/> </privapp-permissions> </permissions> diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java index 7fa422e..88e9a7f 100644 --- a/src/com/google/android/iwlan/ErrorPolicyManager.java +++ b/src/com/google/android/iwlan/ErrorPolicyManager.java @@ -313,16 +313,19 @@ public class ErrorPolicyManager { ret = DataFailCause.SIM_CARD_CHANGED; } else if (error.getErrorType() == IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED) { - // TODO(b/265215821): Add new DataFailCause to match with IwlanError when possible. - ret = DataFailCause.NETWORK_FAILURE; + ret = DataFailCause.IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED; } else if (error.getErrorType() == IwlanError.TUNNEL_NOT_FOUND) { ret = DataFailCause.IWLAN_TUNNEL_NOT_FOUND; } else if (error.getErrorType() == IwlanError.IKE_INIT_TIMEOUT) { - ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT; + ret = DataFailCause.IWLAN_IKE_INIT_TIMEOUT; } else if (error.getErrorType() == IwlanError.IKE_MOBILITY_TIMEOUT) { - ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT; + ret = DataFailCause.IWLAN_IKE_MOBILITY_TIMEOUT; } else if (error.getErrorType() == IwlanError.IKE_DPD_TIMEOUT) { - ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT; + ret = DataFailCause.IWLAN_IKE_DPD_TIMEOUT; + } else if (error.getErrorType() == IwlanError.TUNNEL_TRANSFORM_FAILED) { + ret = DataFailCause.IWLAN_TUNNEL_TRANSFORM_FAILED; + } else if (error.getErrorType() == IwlanError.IKE_NETWORK_LOST_EXCEPTION) { + ret = DataFailCause.IWLAN_IKE_NETWORK_LOST_EXCEPTION; } else if (error.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) { Exception exception = error.getException(); if (exception instanceof IkeProtocolException) { @@ -331,6 +334,9 @@ public class ErrorPolicyManager { case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: ret = DataFailCause.IWLAN_IKEV2_AUTH_FAILURE; break; + case IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE: + ret = DataFailCause.IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE; + break; case IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION: ret = DataFailCause.IWLAN_PDN_CONNECTION_REJECTION; break; @@ -383,7 +389,7 @@ public class ErrorPolicyManager { ret = DataFailCause.IWLAN_CONGESTION; break; default: - ret = DataFailCause.IWLAN_NETWORK_FAILURE; + ret = DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR; break; } } diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java index 0eb3f33..0d564ab 100644 --- a/src/com/google/android/iwlan/IwlanDataService.java +++ b/src/com/google/android/iwlan/IwlanDataService.java @@ -60,13 +60,13 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics; +import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; 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.proto.MetricsAtom; -import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; -import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -81,15 +81,19 @@ import java.util.HashMap; import java.util.List; import java.util.LongSummaryStatistics; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; public class IwlanDataService extends DataService { private static final String TAG = IwlanDataService.class.getSimpleName(); + + private static final String CONTEXT_ATTRIBUTION_TAG = "IWLAN"; private static Context mContext; private IwlanNetworkMonitorCallback mNetworkMonitorCallback; private static boolean sNetworkConnected = false; private static Network sNetwork = null; + private static LinkProperties sLinkProperties = null; @VisibleForTesting Handler mIwlanDataServiceHandler; private HandlerThread mIwlanDataServiceHandlerThread; private static final Map<Integer, IwlanDataServiceProvider> sIwlanDataServiceProviders = @@ -121,15 +125,6 @@ public class IwlanDataService extends DataService { private static Transport sDefaultDataTransport = Transport.UNSPECIFIED_NETWORK; - enum LinkProtocolType { - UNKNOWN, - IPV4, - IPV6, - IPV4V6 - } - - private static LinkProtocolType sLinkProtocolType = LinkProtocolType.UNKNOWN; - // TODO: see if network monitor callback impl can be shared between dataservice and // networkservice // This callback runs in the same thread as IwlanDataServiceHandler @@ -137,7 +132,7 @@ public class IwlanDataService extends DataService { /** Called when the framework connects and has declared a new network ready for use. */ @Override - public void onAvailable(Network network) { + public void onAvailable(@NonNull Network network) { Log.d(TAG, "onAvailable: " + network); } @@ -149,7 +144,7 @@ public class IwlanDataService extends DataService { * is suddenly disconnected. */ @Override - public void onLosing(Network network, int maxMsToLive) { + public void onLosing(@NonNull Network network, int maxMsToLive) { Log.d(TAG, "onLosing: maxMsToLive: " + maxMsToLive + " network: " + network); } @@ -158,7 +153,7 @@ public class IwlanDataService extends DataService { * callback. */ @Override - public void onLost(Network network) { + public void onLost(@NonNull Network network) { Log.d(TAG, "onLost: " + network); IwlanDataService.setConnectedDataSub(INVALID_SUB_ID); IwlanDataService.setNetworkConnected(false, network, Transport.UNSPECIFIED_NETWORK); @@ -166,25 +161,34 @@ public class IwlanDataService extends DataService { /** Called when the network corresponding to this request changes {@link LinkProperties}. */ @Override - public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { + public void onLinkPropertiesChanged( + @NonNull Network network, @NonNull LinkProperties linkProperties) { Log.d(TAG, "onLinkPropertiesChanged: " + linkProperties); - if (isLinkProtocolTypeChanged(linkProperties)) { + + if (!sNetwork.equals(network)) { + Log.d(TAG, "Ignore LinkProperties changes for unused Network."); + return; + } + + if (!sLinkProperties.equals(linkProperties)) { for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) { dp.dnsPrefetchCheck(); + sLinkProperties = linkProperties; + dp.updateNetwork(network, linkProperties); } } } /** Called when access to the specified network is blocked or unblocked. */ @Override - public void onBlockedStatusChanged(Network network, boolean blocked) { + public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) { // TODO: check if we need to handle this Log.d(TAG, "onBlockedStatusChanged: " + network + " BLOCKED:" + blocked); } @Override public void onCapabilitiesChanged( - Network network, NetworkCapabilities networkCapabilities) { + @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { // onCapabilitiesChanged is guaranteed to be called immediately after onAvailable per // API Log.d(TAG, "onCapabilitiesChanged: " + network + " " + networkCapabilities); @@ -401,21 +405,30 @@ public class IwlanDataService extends DataService { Log.d( SUB_TAG, "Tunnel opened!. APN: " + apnName + "linkproperties: " + linkProperties); - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage( - EVENT_TUNNEL_OPENED, - new TunnelOpenedData( - apnName, linkProperties, mIwlanDataServiceProvider))); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage( + EVENT_TUNNEL_OPENED, + new TunnelOpenedData( + apnName, + linkProperties, + mIwlanDataServiceProvider))); } public void onClosed(String apnName, IwlanError error) { Log.d(SUB_TAG, "Tunnel closed!. APN: " + apnName + " Error: " + error); // this is called, when a tunnel that is up, is closed. // the expectation is error==NO_ERROR for user initiated/normal close. - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage( - EVENT_TUNNEL_CLOSED, - new TunnelClosedData(apnName, error, mIwlanDataServiceProvider))); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage( + EVENT_TUNNEL_CLOSED, + new TunnelClosedData( + apnName, + error, + mIwlanDataServiceProvider))); } } @@ -571,7 +584,7 @@ public class IwlanDataService extends DataService { // get reference to resolver mIwlanDataService = iwlanDataService; mIwlanTunnelCallback = new IwlanTunnelCallback(this); - mIwlanTunnelMetrics = new IwlanTunnelMetricsImpl(this, mIwlanDataServiceHandler); + mIwlanTunnelMetrics = new IwlanTunnelMetricsImpl(this, getIwlanDataServiceHandler()); mEpdgSelector = EpdgSelector.getSelectorInstance(mContext, slotIndex); mCalendar = Calendar.getInstance(); mTunnelStats = new IwlanDataTunnelStats(); @@ -582,14 +595,14 @@ public class IwlanDataService extends DataService { events.add(IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT); events.add(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT); events.add(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT); + events.add(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT); events.add(IwlanEventListener.CELLINFO_CHANGED_EVENT); events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT); IwlanEventListener.getInstance(mContext, slotIndex) - .addEventListener(events, mIwlanDataServiceHandler); + .addEventListener(events, getIwlanDataServiceHandler()); } - @VisibleForTesting - EpdgTunnelManager getTunnelManager() { + private EpdgTunnelManager getTunnelManager() { return EpdgTunnelManager.getInstance(mContext, getSlotIndex()); } @@ -803,9 +816,10 @@ public class IwlanDataService extends DataService { networkTransport); } - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage( - EVENT_SETUP_DATA_CALL, setupDataCallData)); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage(EVENT_SETUP_DATA_CALL, setupDataCallData)); } /** @@ -835,9 +849,11 @@ public class IwlanDataService extends DataService { DeactivateDataCallData deactivateDataCallData = new DeactivateDataCallData(cid, reason, callback, this); - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage( - EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData)); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage( + EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData)); } public void forceCloseTunnelsInDeactivatingState() { @@ -872,10 +888,13 @@ public class IwlanDataService extends DataService { */ @Override public void requestDataCallList(DataServiceCallback callback) { - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage( - EVENT_DATA_CALL_LIST_REQUEST, - new DataCallRequestData(callback, IwlanDataServiceProvider.this))); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage( + EVENT_DATA_CALL_LIST_REQUEST, + new DataCallRequestData( + callback, IwlanDataServiceProvider.this))); } @VisibleForTesting @@ -917,6 +936,12 @@ public class IwlanDataService extends DataService { } @VisibleForTesting + @Nullable + public MetricsAtom getMetricsAtomByApn(String apnName) { + return mMetricsAtomForApn.get(apnName); + } + + @VisibleForTesting public IwlanTunnelCallback getIwlanTunnelCallback() { return mIwlanTunnelCallback; } @@ -931,29 +956,31 @@ public class IwlanDataService extends DataService { return mTunnelStats; } - private void updateNetwork(Network network) { - if (network != null) { - for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) { - TunnelState tunnelState = entry.getValue(); - if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGUP) { - // force close tunnels in bringup since IKE lib only supports - // updating network for tunnels that are already up. - // This may not result in actual closing of Ike Session since - // epdg selection may not be complete yet. - tunnelState.setState(TunnelState.TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP); + private void updateNetwork( + @Nullable Network network, @Nullable LinkProperties linkProperties) { + if (mIwlanDataService.isNetworkConnected( + isActiveDataOnOtherSub(getSlotIndex()), + IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()))) { + getTunnelManager().updateNetwork(network, linkProperties); + } + + if (Objects.equals(network, sNetwork)) { + return; + } + for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) { + TunnelState tunnelState = entry.getValue(); + if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGUP) { + // force close tunnels in bringup since IKE lib only supports + // updating network for tunnels that are already up. + // This may not result in actual closing of Ike Session since + // epdg selection may not be complete yet. + tunnelState.setState(TunnelState.TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP); getTunnelManager() .closeTunnel( entry.getKey(), true /* forceClose */, getIwlanTunnelCallback(), getIwlanTunnelMetrics()); - } else { - if (mIwlanDataService.isNetworkConnected( - isActiveDataOnOtherSub(getSlotIndex()), - IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()))) { - getTunnelManager().updateNetwork(network, entry.getKey()); - } - } } } } @@ -1073,7 +1100,7 @@ public class IwlanDataService extends DataService { mIwlanDataService.removeDataServiceProvider(this); IwlanEventListener iwlanEventListener = IwlanEventListener.getInstance(mContext, getSlotIndex()); - iwlanEventListener.removeEventListener(mIwlanDataServiceHandler); + iwlanEventListener.removeEventListener(getIwlanDataServiceHandler()); iwlanEventListener.unregisterContentObserver(); } @@ -1235,6 +1262,9 @@ public class IwlanDataService extends DataService { // Record setup result for the Metrics metricsAtom.setSetupRequestResult(DataServiceCallback.RESULT_SUCCESS); metricsAtom.setIwlanError(iwlanError.getErrorType()); + + metricsAtom.setIwlanErrorWrappedClassnameAndStack(iwlanError); + metricsAtom.setTunnelState(tunnelState.getState()); metricsAtom.setMessageId( IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED); @@ -1329,6 +1359,12 @@ public class IwlanDataService extends DataService { iwlanDataServiceProvider.mWfcEnabled = false; break; + case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT: + iwlanDataServiceProvider = + (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1); + iwlanDataServiceProvider.updateNetwork(sNetwork, sLinkProperties); + break; + case IwlanEventListener.CELLINFO_CHANGED_EVENT: List<CellInfo> cellInfolist = (List<CellInfo>) msg.obj; iwlanDataServiceProvider = @@ -1457,7 +1493,6 @@ public class IwlanDataService extends DataService { TunnelSetupRequest.Builder tunnelReqBuilder = TunnelSetupRequest.builder() .setApnName(dataProfile.getApnSetting().getApnName()) - .setNetwork(sNetwork) .setIsRoaming(isRoaming) .setPduSessionId(pduSessionId) .setApnIpProtocol( @@ -1803,10 +1838,9 @@ public class IwlanDataService extends DataService { } } - @VisibleForTesting /* Note: this api should have valid transport if networkConnected==true */ static void setNetworkConnected( - boolean networkConnected, Network network, Transport transport) { + boolean networkConnected, @NonNull Network network, Transport transport) { boolean hasNetworkChanged = false; boolean hasTransportChanged = false; @@ -1853,10 +1887,6 @@ public class IwlanDataService extends DataService { sNetworkConnected = networkConnected; sDefaultDataTransport = transport; sNetwork = network; - if (!networkConnected) { - // reset link protocol type - sLinkProtocolType = LinkProtocolType.UNKNOWN; - } if (networkConnected) { if (hasTransportChanged) { @@ -1872,9 +1902,13 @@ public class IwlanDataService extends DataService { } // only prefetch dns and updateNetwork if Network has changed if (hasNetworkChanged) { + ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + LinkProperties linkProperties = connectivityManager.getLinkProperties(network); + sLinkProperties = linkProperties; for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) { dp.dnsPrefetchCheck(); - dp.updateNetwork(sNetwork); + dp.updateNetwork(sNetwork, linkProperties); } IwlanHelper.updateCountryCodeWhenNetworkConnected(); } @@ -1889,48 +1923,6 @@ public class IwlanDataService extends DataService { } } - static boolean isLinkProtocolTypeChanged(LinkProperties linkProperties) { - boolean hasIPV4 = false; - boolean hasIPV6 = false; - - LinkProtocolType linkProtocolType = null; - if (linkProperties != null) { - for (LinkAddress linkAddress : linkProperties.getLinkAddresses()) { - InetAddress inetaddr = linkAddress.getAddress(); - // skip linklocal and loopback addresses - if (!inetaddr.isLoopbackAddress() && !inetaddr.isLinkLocalAddress()) { - if (inetaddr instanceof Inet4Address) { - hasIPV4 = true; - } else if (inetaddr instanceof Inet6Address) { - hasIPV6 = true; - } - } - } - - if (hasIPV4 && hasIPV6) { - linkProtocolType = LinkProtocolType.IPV4V6; - } else if (hasIPV4) { - linkProtocolType = LinkProtocolType.IPV4; - } else if (hasIPV6) { - linkProtocolType = LinkProtocolType.IPV6; - } - - if (sLinkProtocolType != linkProtocolType) { - Log.d( - TAG, - "LinkProtocolType was changed from " - + sLinkProtocolType - + " to " - + linkProtocolType); - sLinkProtocolType = linkProtocolType; - return true; - } - return false; - } - Log.w(TAG, "linkProperties is NULL."); - return false; - } - /** * Get the DataServiceProvider associated with the slotId * @@ -1950,10 +1942,6 @@ public class IwlanDataService extends DataService { // TODO: validity check on slot index Log.d(TAG, "Creating provider for " + slotIndex); - if (mIwlanDataServiceHandler == null) { - initHandler(); - } - if (mNetworkMonitorCallback == null) { // start monitoring network and register for default network callback ConnectivityManager connectivityManager = @@ -1961,21 +1949,25 @@ public class IwlanDataService extends DataService { mNetworkMonitorCallback = new IwlanNetworkMonitorCallback(); if (connectivityManager != null) { connectivityManager.registerSystemDefaultNetworkCallback( - mNetworkMonitorCallback, mIwlanDataServiceHandler); + mNetworkMonitorCallback, getIwlanDataServiceHandler()); } Log.d(TAG, "Registered with Connectivity Service"); } IwlanDataServiceProvider dp = new IwlanDataServiceProvider(slotIndex, this); - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage(EVENT_ADD_DATA_SERVICE_PROVIDER, dp)); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage(EVENT_ADD_DATA_SERVICE_PROVIDER, dp)); return dp; } public void removeDataServiceProvider(IwlanDataServiceProvider dp) { - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage(EVENT_REMOVE_DATA_SERVICE_PROVIDER, dp)); + getIwlanDataServiceHandler() + .sendMessage( + getIwlanDataServiceHandler() + .obtainMessage(EVENT_REMOVE_DATA_SERVICE_PROVIDER, dp)); } @VisibleForTesting @@ -2013,8 +2005,12 @@ public class IwlanDataService extends DataService { } @VisibleForTesting - void initHandler() { - mIwlanDataServiceHandler = new IwlanDataServiceHandler(getLooper()); + @NonNull + Handler getIwlanDataServiceHandler() { + if (mIwlanDataServiceHandler == null) { + mIwlanDataServiceHandler = new IwlanDataServiceHandler(getLooper()); + } + return mIwlanDataServiceHandler; } @VisibleForTesting @@ -2050,6 +2046,8 @@ public class IwlanDataService extends DataService { return "WIFI_CALLING_ENABLE_EVENT"; case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT: return "WIFI_CALLING_DISABLE_EVENT"; + case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT: + return "CROSS_SIM_CALLING_ENABLE_EVENT"; case IwlanEventListener.CELLINFO_CHANGED_EVENT: return "CELLINFO_CHANGED_EVENT"; case EVENT_TUNNEL_OPENED_METRICS: @@ -2065,7 +2063,8 @@ public class IwlanDataService extends DataService { @Override public void onCreate() { - setAppContext(getApplicationContext()); + Context context = getApplicationContext().createAttributionContext(CONTEXT_ATTRIBUTION_TAG); + setAppContext(context); IwlanBroadcastReceiver.startListening(mContext); IwlanHelper.startCountryDetector(mContext); } @@ -2077,15 +2076,15 @@ public class IwlanDataService extends DataService { @Override public IBinder onBind(Intent intent) { - Log.d(TAG, "Iwlanservice onBind"); + Log.d(TAG, "IwlanDataService onBind"); return super.onBind(intent); } @Override public boolean onUnbind(Intent intent) { - Log.d(TAG, "IwlanService onUnbind"); - mIwlanDataServiceHandler.sendMessage( - mIwlanDataServiceHandler.obtainMessage(EVENT_FORCE_CLOSE_TUNNEL)); + Log.d(TAG, "IwlanDataService onUnbind"); + getIwlanDataServiceHandler() + .sendMessage(getIwlanDataServiceHandler().obtainMessage(EVENT_FORCE_CLOSE_TUNNEL)); return super.onUnbind(intent); } diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java index efda5b7..ade7189 100644 --- a/src/com/google/android/iwlan/IwlanHelper.java +++ b/src/com/google/android/iwlan/IwlanHelper.java @@ -110,14 +110,15 @@ public class IwlanHelper { return info; } - public static List<InetAddress> getAddressesForNetwork(Network network, Context context) { + // Retrieves all IP addresses for this Network, including stacked IPv4 link addresses. + public static List<InetAddress> getAllAddressesForNetwork(Network network, Context context) { ConnectivityManager connectivityManager = context.getSystemService(ConnectivityManager.class); List<InetAddress> gatewayList = new ArrayList<>(); if (network != null) { LinkProperties linkProperties = connectivityManager.getLinkProperties(network); if (linkProperties != null) { - for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) { + for (LinkAddress linkAddr : linkProperties.getAllLinkAddresses()) { InetAddress inetAddr = linkAddr.getAddress(); // skip linklocal and loopback addresses if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) { @@ -132,25 +133,6 @@ public class IwlanHelper { return gatewayList; } - public static List<InetAddress> getStackedAddressesForNetwork( - Network network, Context context) { - ConnectivityManager connectivityManager = - context.getSystemService(ConnectivityManager.class); - List<InetAddress> gatewayList = new ArrayList<>(); - if (network != null) { - LinkProperties linkProperties = connectivityManager.getLinkProperties(network); - if (linkProperties != null) { - for (LinkAddress linkAddr : linkProperties.getAllLinkAddresses()) { - InetAddress inetAddr = linkAddr.getAddress(); - if ((inetAddr instanceof Inet4Address)) { - gatewayList.add(inetAddr); - } - } - } - } - return gatewayList; - } - /** * The method is to check if this IP address is an IPv4-embedded IPv6 address(Pref64::/n). * @@ -162,22 +144,24 @@ public class IwlanHelper { } public static boolean hasIpv6Address(List<InetAddress> localAddresses) { - for (InetAddress address : localAddresses) { - if (address instanceof Inet6Address) { - return true; + if (localAddresses != null) { + for (InetAddress address : localAddresses) { + if (address instanceof Inet6Address) { + return true; + } } } - return false; } public static boolean hasIpv4Address(List<InetAddress> localAddresses) { - for (InetAddress address : localAddresses) { - if (address instanceof Inet4Address) { - return true; + if (localAddresses != null) { + for (InetAddress address : localAddresses) { + if (address instanceof Inet4Address) { + return true; + } } } - return false; } diff --git a/src/com/google/android/iwlan/IwlanNetworkService.java b/src/com/google/android/iwlan/IwlanNetworkService.java index d76c883..00f0907 100644 --- a/src/com/google/android/iwlan/IwlanNetworkService.java +++ b/src/com/google/android/iwlan/IwlanNetworkService.java @@ -185,15 +185,18 @@ public class IwlanNetworkService extends NetworkService { events.add(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT); events.add(IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT); IwlanEventListener.getInstance(mContext, slotIndex) - .addEventListener(events, mIwlanNetworkServiceHandler); + .addEventListener(events, getIwlanNetworkServiceHandler()); } @Override public void requestNetworkRegistrationInfo(int domain, NetworkServiceCallback callback) { - mIwlanNetworkServiceHandler.sendMessage( - mIwlanNetworkServiceHandler.obtainMessage( - EVENT_NETWORK_REGISTRATION_INFO_REQUEST, - new NetworkRegistrationInfoRequestData(domain, callback, this))); + getIwlanNetworkServiceHandler() + .sendMessage( + getIwlanNetworkServiceHandler() + .obtainMessage( + EVENT_NETWORK_REGISTRATION_INFO_REQUEST, + new NetworkRegistrationInfoRequestData( + domain, callback, this))); } /** @@ -205,7 +208,7 @@ public class IwlanNetworkService extends NetworkService { public void close() { mIwlanNetworkService.removeNetworkServiceProvider(this); IwlanEventListener.getInstance(mContext, getSlotIndex()) - .removeEventListener(mIwlanNetworkServiceHandler); + .removeEventListener(getIwlanNetworkServiceHandler()); } @VisibleForTesting @@ -359,14 +362,11 @@ public class IwlanNetworkService extends NetworkService { // TODO: validity check slot index - if (mIwlanNetworkServiceHandler == null) { - initHandler(); - } - IwlanNetworkServiceProvider np = new IwlanNetworkServiceProvider(slotIndex, this); - mIwlanNetworkServiceHandler.sendMessage( - mIwlanNetworkServiceHandler.obtainMessage( - EVENT_CREATE_NETWORK_SERVICE_PROVIDER, np)); + getIwlanNetworkServiceHandler() + .sendMessage( + getIwlanNetworkServiceHandler() + .obtainMessage(EVENT_CREATE_NETWORK_SERVICE_PROVIDER, np)); return np; } @@ -431,9 +431,10 @@ public class IwlanNetworkService extends NetworkService { } public void removeNetworkServiceProvider(IwlanNetworkServiceProvider np) { - mIwlanNetworkServiceHandler.sendMessage( - mIwlanNetworkServiceHandler.obtainMessage( - EVENT_REMOVE_NETWORK_SERVICE_PROVIDER, np)); + getIwlanNetworkServiceHandler() + .sendMessage( + getIwlanNetworkServiceHandler() + .obtainMessage(EVENT_REMOVE_NETWORK_SERVICE_PROVIDER, np)); } void initCallback() { @@ -441,14 +442,14 @@ public class IwlanNetworkService extends NetworkService { mNetworkMonitorCallback = new IwlanNetworkMonitorCallback(); getConnectivityManager() .registerSystemDefaultNetworkCallback( - mNetworkMonitorCallback, mIwlanNetworkServiceHandler); + mNetworkMonitorCallback, getIwlanNetworkServiceHandler()); Log.d(TAG, "Registered with Connectivity Service"); /* register with subscription manager */ mSubsChangeListener = new IwlanOnSubscriptionsChangedListener(); getSubscriptionManager() .addOnSubscriptionsChangedListener( - new HandlerExecutor(mIwlanNetworkServiceHandler), mSubsChangeListener); + new HandlerExecutor(getIwlanNetworkServiceHandler()), mSubsChangeListener); Log.d(TAG, "Registered with Subscription Service"); } @@ -483,8 +484,12 @@ public class IwlanNetworkService extends NetworkService { } @VisibleForTesting - void initHandler() { - mIwlanNetworkServiceHandler = new IwlanNetworkServiceHandler(getLooper()); + @NonNull + Handler getIwlanNetworkServiceHandler() { + if (mIwlanNetworkServiceHandler == null) { + mIwlanNetworkServiceHandler = new IwlanNetworkServiceHandler(getLooper()); + } + return mIwlanNetworkServiceHandler; } @VisibleForTesting diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java index d6bc103..4534b81 100644 --- a/src/com/google/android/iwlan/epdg/EpdgSelector.java +++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java @@ -80,12 +80,16 @@ public class EpdgSelector { private byte[] mV6PcoData = null; @NonNull private final ErrorPolicyManager mErrorPolicyManager; - private static final long DNS_RESOLVER_TIMEOUT_DURATION_SEC = 5L; + // 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; - private static final long PARALLEL_DNS_RESOLVER_TIMEOUT_DURATION_SEC = 20L; + private static final long PARALLEL_STATIC_RESOLUTION_TIMEOUT_DURATION_SEC = 6L; + private static final long PARALLEL_PLMN_RESOLUTION_TIMEOUT_DURATION_SEC = 20L; private static final int NUM_EPDG_SELECTION_EXECUTORS = 2; // 1 each for normal selection, SOS. private static final int MAX_EPDG_SELECTION_THREADS = 2; // 1 each for prefetch, tunnel bringup. private static final int MAX_DNS_RESOLVER_THREADS = 25; // Do not expect > 25 FQDNs per carrier. + private static final String NO_DOMAIN = "NO_DOMAIN"; BlockingQueue<Runnable> dnsResolutionQueue = new ArrayBlockingQueue<>( @@ -142,7 +146,7 @@ public class EpdgSelector { public interface EpdgSelectorCallback { /*gives priority ordered list of addresses*/ - void onServerListChanged(int transactionId, ArrayList<InetAddress> validIPList); + void onServerListChanged(int transactionId, List<InetAddress> validIPList); void onError(int transactionId, IwlanError error); } @@ -204,7 +208,7 @@ public class EpdgSelector { } private CompletableFuture<Map.Entry<String, List<InetAddress>>> submitDnsResolverQuery( - String domainName, Network network, Executor executor) { + String domainName, Network network, int queryType, Executor executor) { CompletableFuture<Map.Entry<String, List<InetAddress>>> result = new CompletableFuture(); final DnsResolver.Callback<List<InetAddress>> cb = @@ -232,7 +236,7 @@ public class EpdgSelector { } }; DnsResolver.getInstance() - .query(network, domainName, DnsResolver.FLAG_EMPTY, executor, null, cb); + .query(network, domainName, queryType, DnsResolver.FLAG_EMPTY, executor, null, cb); return result; } @@ -278,6 +282,22 @@ public class EpdgSelector { .collect(Collectors.<T>toList())); } + @VisibleForTesting + protected boolean hasIpv4Address(Network network) { + return IwlanHelper.hasIpv4Address(IwlanHelper.getAllAddressesForNetwork(network, mContext)); + } + + @VisibleForTesting + protected boolean hasIpv6Address(Network network) { + return IwlanHelper.hasIpv6Address(IwlanHelper.getAllAddressesForNetwork(network, mContext)); + } + + private void printParallelDnsResult(Map<String, List<InetAddress>> domainNameToIpAddresses) { + Log.d(TAG, "Parallel DNS resolution result:"); + for (String domain : domainNameToIpAddresses.keySet()) { + Log.d(TAG, domain + ": " + domainNameToIpAddresses.get(domain)); + } + } /** * 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. @@ -285,27 +305,46 @@ public class EpdgSelector { * @param domainNames Domain names for which DNS resolution needs to be performed. * @param filter Selects for IPv4, IPv6 (or both) addresses from the resulting DNS records * @param network {@link Network} Network on which to run the DNS query. + * @param timeout timeout in seconds. * @return List of unique IP addresses corresponding to the domainNames. */ private LinkedHashMap<String, List<InetAddress>> getIP( - List<String> domainNames, int filter, Network network) { + List<String> domainNames, int filter, Network network, long timeout) { // LinkedHashMap preserves insertion order (and hence priority) of domain names passed in. LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = new LinkedHashMap<>(); List<CompletableFuture<Map.Entry<String, List<InetAddress>>>> futuresList = new ArrayList<>(); for (String domainName : domainNames) { + if (InetAddresses.isNumericAddress(domainName)) { + Log.d(TAG, domainName + " is a numeric IP address!"); + InetAddress inetAddr = InetAddresses.parseNumericAddress(domainName); + domainNameToIpAddr.put(NO_DOMAIN, new ArrayList<>(List.of(inetAddr))); + continue; + } + domainNameToIpAddr.put(domainName, new ArrayList<>()); - futuresList.add(submitDnsResolverQuery(domainName, network, mDnsResolutionExecutor)); + // Dispatches separate IPv4 and IPv6 queries to avoid being blocked on either result. + if (hasIpv4Address(network)) { + futuresList.add( + submitDnsResolverQuery( + domainName, network, DnsResolver.TYPE_A, mDnsResolutionExecutor)); + } + if (hasIpv6Address(network)) { + futuresList.add( + submitDnsResolverQuery( + domainName, + network, + DnsResolver.TYPE_AAAA, + mDnsResolutionExecutor)); + } } CompletableFuture<List<Map.Entry<String, List<InetAddress>>>> allFuturesResult = allOf(futuresList); List<Map.Entry<String, List<InetAddress>>> resultList = null; try { - resultList = - allFuturesResult.get( - PARALLEL_DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS); + resultList = allFuturesResult.get(timeout, TimeUnit.SECONDS); } catch (ExecutionException e) { Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); } catch (InterruptedException e) { @@ -318,8 +357,17 @@ public class EpdgSelector { Log.w(TAG, "No IP addresses in parallel DNS query!"); } else { for (Map.Entry<String, List<InetAddress>> entry : resultList) { - domainNameToIpAddr.put( - entry.getKey(), v4v6ProtocolFilter(entry.getValue(), filter)); + String resultDomainName = entry.getKey(); + List<InetAddress> resultIpAddr = v4v6ProtocolFilter(entry.getValue(), filter); + + if (!domainNameToIpAddr.containsKey(resultDomainName)) { + Log.w( + TAG, + "Unexpected domain name in DnsResolver result: " + + resultDomainName); + continue; + } + domainNameToIpAddr.get(resultDomainName).addAll(resultIpAddr); } } } @@ -343,12 +391,8 @@ public class EpdgSelector { Log.d(TAG, "Input domainName : " + domainName); if (InetAddresses.isNumericAddress(domainName)) { - try { - Log.d(TAG, domainName + " is a numeric ip address"); - ipList.add(InetAddress.getByName(domainName)); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e); - } + Log.d(TAG, domainName + " is a numeric IP address!"); + ipList.add(InetAddresses.parseNumericAddress(domainName)); } else { try { CompletableFuture<List<InetAddress>> result = new CompletableFuture(); @@ -398,15 +442,7 @@ public class EpdgSelector { } private String[] getPlmnList() { - List<String> plmnsFromSubInfo = new ArrayList<>(); - - List<String> plmnsFromCarrierConfig = - new ArrayList<>( - Arrays.asList( - IwlanHelper.getConfig( - CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, - mContext, - mSlotId))); + List<String> plmnsFromCarrierConfig = getPlmnsFromCarrierConfig(); Log.d(TAG, "plmnsFromCarrierConfig:" + plmnsFromCarrierConfig); // Get Ehplmns & mccmnc from SubscriptionManager @@ -424,48 +460,68 @@ public class EpdgSelector { return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); } - // There are three sources of plmns - sim plmn, plmn list from carrier config and - // Ehplmn list from subscription info. - // The plmns are prioritized as follows: - // 1. Sim plmn - // 2. Plmns common to both lists. - // 3. Remaining plmns in the lists. - List<String> combinedList = new ArrayList<>(); // Get MCCMNC from IMSI - String plmnFromImsi = subInfo.getMccString() + "-" + subInfo.getMncString(); - combinedList.add(plmnFromImsi); - - // Get Ehplmns from TelephonyManager - for (String ehplmn : getEhplmns()) { - if (ehplmn.length() == 5 || ehplmn.length() == 6) { - StringBuilder str = new StringBuilder(ehplmn); - str.insert(3, "-"); - plmnsFromSubInfo.add(str.toString()); - } - } + String plmnFromImsi = subInfo.getMccString() + subInfo.getMncString(); - Log.d(TAG, "plmnsFromSubInfo:" + plmnsFromSubInfo); + int[] prioritizedPlmnTypes = + IwlanHelper.getConfig( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + mContext, + mSlotId); - // To avoid double adding plmn from imsi - plmnsFromCarrierConfig.removeIf(i -> i.equals(plmnFromImsi)); - plmnsFromSubInfo.removeIf(i -> i.equals(plmnFromImsi)); + List<String> ehplmns = getEhplmns(); + String registeredPlmn = getRegisteredPlmn(); - for (Iterator<String> iterator = plmnsFromCarrierConfig.iterator(); iterator.hasNext(); ) { - String plmn = iterator.next(); - if (plmnsFromSubInfo.contains(plmn)) { - combinedList.add(plmn); - plmnsFromSubInfo.remove(plmn); - iterator.remove(); + List<String> combinedList = new ArrayList<>(); + for (int plmnType : prioritizedPlmnTypes) { + switch (plmnType) { + case CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN: + if (isInEpdgSelectionInfo(registeredPlmn)) { + combinedList.add(registeredPlmn); + } + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN: + combinedList.add(plmnFromImsi); + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL: + combinedList.addAll(getEhplmns()); + break; + case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_FIRST: + if (!ehplmns.isEmpty()) { + combinedList.add(ehplmns.get(0)); + } + break; + default: + Log.e(TAG, "Unknown PLMN type: " + plmnType); + break; } } - combinedList.addAll(plmnsFromSubInfo); - combinedList.addAll(plmnsFromCarrierConfig); + combinedList = + combinedList.stream() + .distinct() + .filter(EpdgSelector::isValidPlmn) + .map(plmn -> new StringBuilder(plmn).insert(3, "-").toString()) + .toList(); Log.d(TAG, "Final plmn list:" + combinedList); return combinedList.toArray(new String[combinedList.size()]); } + private List<String> getPlmnsFromCarrierConfig() { + return Arrays.asList( + IwlanHelper.getConfig( + CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, mContext, mSlotId)); + } + + private boolean isInEpdgSelectionInfo(String plmn) { + if (!isValidPlmn(plmn)) { + return false; + } + List<String> plmnsFromCarrierConfig = getPlmnsFromCarrierConfig(); + return plmnsFromCarrierConfig.contains(new StringBuilder(plmn).insert(3, "-").toString()); + } + private ArrayList<InetAddress> removeDuplicateIp(List<InetAddress> validIpList) { ArrayList<InetAddress> resultIpList = new ArrayList<InetAddress>(); @@ -499,6 +555,25 @@ public class EpdgSelector { return mccmnc; } + /** + * @return the registered PLMN, null if not registered with 3gpp or failed to get telephony + * manager + */ + @Nullable + private String getRegisteredPlmn() { + TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); + if (telephonyManager == null) { + Log.e(TAG, "TelephonyManager is NULL"); + return null; + } + + telephonyManager = + telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); + + String registeredPlmn = telephonyManager.getNetworkOperator(); + return registeredPlmn.isEmpty() ? null : registeredPlmn; + } + private List<String> getEhplmns() { TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); mTelephonyManager = @@ -537,9 +612,14 @@ public class EpdgSelector { } Log.d(TAG, "Static Domain Names: " + Arrays.toString(domainNames)); - for (String domainName : domainNames) { - getIP(domainName, filter, validIpList, network); - } + LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = + getIP( + Arrays.asList(domainNames), + filter, + network, + PARALLEL_STATIC_RESOLUTION_TIMEOUT_DURATION_SEC); + printParallelDnsResult(domainNameToIpAddr); + domainNameToIpAddr.values().forEach(validIpList::addAll); } private String[] getDomainNames(String key) { @@ -614,7 +694,8 @@ public class EpdgSelector { } LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = - getIP(domainNames, filter, network); + getIP(domainNames, filter, network, PARALLEL_PLMN_RESOLUTION_TIMEOUT_DURATION_SEC); + printParallelDnsResult(domainNameToIpAddr); domainNameToIpAddr.values().forEach(validIpList::addAll); return domainNameToIpAddr; } @@ -1180,4 +1261,14 @@ public class EpdgSelector { return new IwlanError(IwlanError.NO_ERROR); } + + /** + * Validates a PLMN (Public Land Mobile Network) identifier string. + * + * @param plmn The PLMN identifier string to validate. + * @return True if the PLMN identifier is valid, false otherwise. + */ + private static boolean isValidPlmn(String plmn) { + return plmn != null && plmn.matches("\\d{5,6}"); + } } diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java index 2cf0797..b8b70e4 100644 --- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java +++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java @@ -28,6 +28,7 @@ import android.net.IpPrefix; import android.net.IpSecManager; import android.net.IpSecTransform; import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.Network; import android.net.eap.EapAkaInfo; import android.net.eap.EapInfo; @@ -76,10 +77,10 @@ import com.google.android.iwlan.ErrorPolicyManager; import com.google.android.iwlan.IwlanError; import com.google.android.iwlan.IwlanHelper; import com.google.android.iwlan.IwlanTunnelMetricsImpl; -import com.google.android.iwlan.exceptions.IwlanSimNotReadyException; import com.google.android.iwlan.TunnelMetricsInterface; -import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics; +import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics; +import com.google.android.iwlan.exceptions.IwlanSimNotReadyException; import java.io.IOException; import java.io.PrintWriter; @@ -146,14 +147,22 @@ public class EpdgTunnelManager { private static final String TRAFFIC_SELECTOR_IPV6_END_ADDR = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; + // "192.0.2.0" is selected from RFC5737, "IPv4 Address Blocks Reserved for Documentation" + private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); + private static final Map<Integer, EpdgTunnelManager> mTunnelManagerInstances = new ConcurrentHashMap<>(); private Queue<TunnelRequestWrapper> mPendingBringUpRequests = new LinkedList<>(); private final EpdgInfo mValidEpdgInfo = new EpdgInfo(); - private InetAddress mEpdgAddress; - private Network mNetwork; + @Nullable private InetAddress mEpdgAddress; + + // The most recently updated system default network as seen by IwlanDataService. + @Nullable private Network mDefaultNetwork; + // The latest Network provided to the IKE session. Only for debugging purposes. + @Nullable private Network mIkeSessionNetwork; + private int mTransactionId = 0; private boolean mHasConnectedToEpdg; private final IkeSessionCreator mIkeSessionCreator; @@ -163,8 +172,6 @@ public class EpdgTunnelManager { private final String TAG; - private List<InetAddress> mLocalAddresses; - @Nullable private byte[] mNextReauthId = null; private long mEpdgServerSelectionDuration = 0; private long mEpdgServerSelectionStartTime = 0; @@ -216,13 +223,10 @@ public class EpdgTunnelManager { SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512); } - private IkeSessionState mIkeSessionState; - private final EpdgSelector.EpdgSelectorCallback mSelectorCallback = new EpdgSelector.EpdgSelectorCallback() { @Override - public void onServerListChanged( - int transactionId, ArrayList<InetAddress> validIPList) { + public void onServerListChanged(int transactionId, List<InetAddress> validIPList) { sendSelectionRequestComplete( validIPList, new IwlanError(IwlanError.NO_ERROR), transactionId); } @@ -248,6 +252,16 @@ public class EpdgTunnelManager { private boolean mIsBackoffTimeValid = false; private long mBackoffTime; + private IkeSessionState mIkeSessionState; + + public IkeSessionState getIkeSessionState() { + return mIkeSessionState; + } + + public void setIkeSessionState(IkeSessionState ikeSessionState) { + mIkeSessionState = ikeSessionState; + } + public NetworkSliceInfo getSliceInfo() { return mSliceInfo; } @@ -285,6 +299,8 @@ public class EpdgTunnelManager { mError = new IwlanError(IwlanError.NO_ERROR); mSrcIpv6Address = srcIpv6Addr; mSrcIpv6AddressPrefixLen = srcIpv6PrefixLength; + + setIkeSessionState(IkeSessionState.IKE_SESSION_INIT_IN_PROGRESS); } @NonNull @@ -427,8 +443,7 @@ public class EpdgTunnelManager { mHandler.sendMessage( mHandler.obtainMessage( EVENT_IKE_SESSION_CLOSED, - new SessionClosedData( - mApnName, mToken, new IwlanError(IwlanError.NO_ERROR)))); + new SessionClosedData(mApnName, mToken, null /* ikeException */))); } @Override @@ -520,8 +535,7 @@ public class EpdgTunnelManager { mHandler.sendMessage( mHandler.obtainMessage( EVENT_CHILD_SESSION_CLOSED, - new SessionClosedData( - mApnName, mToken, new IwlanError(IwlanError.NO_ERROR)))); + new SessionClosedData(mApnName, mToken, null /* ikeException */))); } @Override @@ -665,13 +679,12 @@ public class EpdgTunnelManager { * Update the local Network. This will trigger a revaluation for every tunnel for which tunnel * manager has state. * - * <p>Tunnels in bringup state will be for closed since IKE currently keeps retrying. - * - * <p>For rest of the tunnels, update IKE session wth new network. This will either result in - * MOBIKE callflow or just a rekey over new Network + * @param network the network to be updated + * @param network the linkProperties to be updated */ - public void updateNetwork(@NonNull Network network, String apnName) { - UpdateNetworkWrapper updateNetworkWrapper = new UpdateNetworkWrapper(network, apnName); + public void updateNetwork(Network network, LinkProperties linkProperties) { + UpdateNetworkWrapper updateNetworkWrapper = + new UpdateNetworkWrapper(network, linkProperties); mHandler.sendMessage(mHandler.obtainMessage(EVENT_UPDATE_NETWORK, updateNetworkWrapper)); } /** @@ -713,8 +726,6 @@ public class EpdgTunnelManager { TunnelRequestWrapper tunnelRequestWrapper = new TunnelRequestWrapper(setupRequest, tunnelCallback, tunnelMetrics); - mIkeSessionState = IkeSessionState.NO_IKE_SESSION; - mHandler.sendMessage( mHandler.obtainMessage(EVENT_TUNNEL_BRINGUP_REQUEST, tunnelRequestWrapper)); @@ -732,7 +743,7 @@ public class EpdgTunnelManager { TAG, "Bringing up tunnel for apn: " + apnName - + "ePDG : " + + " ePDG: " + mEpdgAddress.getHostAddress()); final int token = incrementAndGetCurrentTokenForApn(apnName); @@ -757,7 +768,7 @@ public class EpdgTunnelManager { Executors.newSingleThreadExecutor(), getTmIkeSessionCallback(apnName, token), new TmChildSessionCallback(apnName, token)); - mIkeSessionState = IkeSessionState.IKE_SESSION_INIT_IN_PROGRESS; + boolean isSrcIpv6Present = setupRequest.srcIpv6Address().isPresent(); putApnNameToTunnelConfig( apnName, @@ -954,7 +965,7 @@ public class EpdgTunnelManager { .setRemoteIdentification(getId(setupRequest.apnName(), false)) .setAuthEap(null, getEapConfig()) .addIkeSaProposal(buildIkeSaProposal()) - .setNetwork(mNetwork) + .setNetwork(mDefaultNetwork) .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID) .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE) .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY) @@ -982,6 +993,12 @@ public class EpdgTunnelManager { } } + // If MOBIKE is configured, ePDGs may force IPv6 UDP encapsulation- as specified by + // RFC 4555- which Android connectivity stack presently does not support. + if (mEpdgAddress instanceof Inet6Address) { + builder.removeIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE); + } + Ike3gppParams.Builder builder3gppParams = null; // TODO(b/239753287): Telus carrier requests DEVICE_IDENTITY, but errors out when parsing @@ -997,9 +1014,13 @@ public class EpdgTunnelManager { } } - if (setupRequest.pduSessionId() != 0) { - builder3gppParams = new Ike3gppParams.Builder(); - builder3gppParams.setPduSessionId((byte) setupRequest.pduSessionId()); + if (isN1ModeSupported()) { + if (setupRequest.pduSessionId() != 0) { + // Configures the PduSession ID in N1_MODE_CAPABILITY payload + // to notify the server that UE supports N1_MODE + builder3gppParams = new Ike3gppParams.Builder(); + builder3gppParams.setPduSessionId((byte) setupRequest.pduSessionId()); + } } if (builder3gppParams != null) { @@ -1234,12 +1255,6 @@ public class EpdgTunnelManager { private void onSessionClosedWithException( IkeException exception, String apnName, int token, int sessionType) { - IwlanError error; - if (exception instanceof IkeIOException) { - error = new IwlanError(mIkeSessionState.getErrorType(), exception); - } else { - error = new IwlanError(exception); - } Log.e( TAG, "Closing tunnel with exception for apn: " @@ -1247,15 +1262,12 @@ public class EpdgTunnelManager { + " with token: " + token + " sessionType:" - + sessionType - + " error: " - + error - + " state: " - + mIkeSessionState); + + sessionType); exception.printStackTrace(); mHandler.sendMessage( - mHandler.obtainMessage(sessionType, new SessionClosedData(apnName, token, error))); + mHandler.obtainMessage( + sessionType, new SessionClosedData(apnName, token, exception))); } private boolean isEpdgSelectionOrFirstTunnelBringUpInProgress() { @@ -1267,8 +1279,19 @@ public class EpdgTunnelManager { || !mPendingBringUpRequests.isEmpty(); } + private IwlanError getErrorFromIkeException( + IkeException ikeException, IkeSessionState ikeSessionState) { + IwlanError error; + if (ikeException instanceof IkeIOException) { + error = new IwlanError(ikeSessionState.getErrorType(), ikeException); + } else { + error = new IwlanError(ikeException); + } + Log.e(TAG, "Closing tunnel: error: " + error + " state: " + ikeSessionState); + return error; + } + private final class TmHandler extends Handler { - private final String TAG = TmHandler.class.getSimpleName(); @Override public void handleMessage(Message msg) { @@ -1302,6 +1325,7 @@ public class EpdgTunnelManager { case EVENT_TUNNEL_BRINGUP_REQUEST: TunnelRequestWrapper tunnelRequestWrapper = (TunnelRequestWrapper) msg.obj; TunnelSetupRequest setupRequest = tunnelRequestWrapper.getSetupRequest(); + IwlanError bringUpError = null; onClosedMetricsBuilder = new OnClosedMetrics.Builder().setApnName(setupRequest.apnName()); @@ -1309,24 +1333,21 @@ public class EpdgTunnelManager { if (IwlanHelper.getSubId(mContext, mSlotId) == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { Log.e(TAG, "SIM isn't ready"); - IwlanError iwlanError = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION); - reportIwlanError(setupRequest.apnName(), iwlanError); - tunnelRequestWrapper - .getTunnelCallback() - .onClosed(setupRequest.apnName(), iwlanError); - tunnelRequestWrapper - .getTunnelMetrics() - .onClosed(onClosedMetricsBuilder.build()); - return; + bringUpError = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION); + reportIwlanError(setupRequest.apnName(), bringUpError); + } else if (Objects.isNull(mDefaultNetwork)) { + Log.e(TAG, "The default network is not ready"); + bringUpError = new IwlanError(IwlanError.IKE_INTERNAL_IO_EXCEPTION); + reportIwlanError(setupRequest.apnName(), bringUpError); + } else if (!canBringUpTunnel(setupRequest.apnName())) { + Log.d(TAG, "Cannot bring up tunnel as retry time has not passed"); + bringUpError = getLastError(setupRequest.apnName()); } - if (!canBringUpTunnel(setupRequest.apnName())) { - Log.d(TAG, "Cannot bring up tunnel as retry time has not passed"); + if (Objects.nonNull(bringUpError)) { tunnelRequestWrapper .getTunnelCallback() - .onClosed( - setupRequest.apnName(), - getLastError(setupRequest.apnName())); + .onClosed(setupRequest.apnName(), bringUpError); tunnelRequestWrapper .getTunnelMetrics() .onClosed(onClosedMetricsBuilder.build()); @@ -1344,7 +1365,6 @@ public class EpdgTunnelManager { if (!isEpdgSelectionOrFirstTunnelBringUpInProgress()) { // No tunnel bring-up in progress. Select the ePDG address first - mNetwork = setupRequest.network(); selectEpdgAddress(setupRequest); } @@ -1431,11 +1451,11 @@ public class EpdgTunnelManager { (int) mIkeTunnelEstablishmentDuration) .build()); - setHasConnectedToEpdg(true); + onConnectedToEpdg(true); mValidEpdgInfo.resetIndex(); printRequestQueue("EVENT_CHILD_SESSION_OPENED"); serviceAllPendingRequests(); - mIkeSessionState = IkeSessionState.CHILD_SESSION_OPENED; + tunnelConfig.setIkeSessionState(IkeSessionState.CHILD_SESSION_OPENED); break; case EVENT_IKE_SESSION_CLOSED: @@ -1455,8 +1475,11 @@ public class EpdgTunnelManager { // the Child session closed exceptionally; in which case, we attempt to retrieve // the stored error (if any) from TunnelConfig. IwlanError iwlanError; - if (sessionClosedData.mIwlanError.getErrorType() != IwlanError.NO_ERROR) { - iwlanError = sessionClosedData.mIwlanError; + if (sessionClosedData.mIkeException != null) { + iwlanError = + getErrorFromIkeException( + sessionClosedData.mIkeException, + tunnelConfig.getIkeSessionState()); } else { // If IKE session opened, then closed before child session (and IWLAN // tunnel) opened. @@ -1484,6 +1507,7 @@ public class EpdgTunnelManager { } Log.d(TAG, "Tunnel Closed: " + iwlanError); + tunnelConfig.setIkeSessionState(IkeSessionState.NO_IKE_SESSION); tunnelConfig.getTunnelCallback().onClosed(apnName, iwlanError); onClosedMetricsBuilder = new OnClosedMetrics.Builder().setApnName(apnName); @@ -1508,31 +1532,54 @@ public class EpdgTunnelManager { mApnNameToTunnelConfig.remove(apnName); if (mApnNameToTunnelConfig.size() == 0 && mPendingBringUpRequests.isEmpty()) { - resetTunnelManagerState(); + onConnectedToEpdg(false); } - mIkeSessionState = IkeSessionState.NO_IKE_SESSION; - break; case EVENT_UPDATE_NETWORK: UpdateNetworkWrapper updatedNetwork = (UpdateNetworkWrapper) msg.obj; - apnName = updatedNetwork.getApnName(); - Network network = updatedNetwork.getNetwork(); - tunnelConfig = mApnNameToTunnelConfig.get(apnName); + mDefaultNetwork = updatedNetwork.getNetwork(); + LinkProperties defaultLinkProperties = updatedNetwork.getLinkProperties(); + String paraString = "Network: " + mDefaultNetwork; - // Update the global cache if they aren't equal - if (mNetwork == null || !mNetwork.equals(network)) { - Log.d(TAG, "Updating mNetwork to " + network); - mNetwork = network; - } - - if (tunnelConfig == null) { - Log.d(TAG, "Update Network request: No tunnel exists for apn: " + apnName); - } else { - Log.d(TAG, "Updating Network for apn: " + apnName + " Network: " + network); - tunnelConfig.getIkeSession().setNetwork(network); - mIkeSessionState = IkeSessionState.IKE_MOBILITY_IN_PROGRESS; + if (mHasConnectedToEpdg) { + if (Objects.isNull(mDefaultNetwork)) { + Log.w(TAG, "The default network has been removed."); + } else if (Objects.isNull(defaultLinkProperties)) { + Log.w( + TAG, + "The default network's LinkProperties is not ready ." + + paraString); + } else if (!defaultLinkProperties.isReachable(mEpdgAddress)) { + Log.w( + TAG, + "The default network link " + + defaultLinkProperties + + " doesn't have a route to the ePDG " + + mEpdgAddress + + paraString); + } else if (Objects.equals(mDefaultNetwork, mIkeSessionNetwork)) { + Log.w( + TAG, + "The default network has not changed from the IKE session" + + " network. " + + paraString); + } else { + mApnNameToTunnelConfig.forEach( + (apn, config) -> { + Log.d( + TAG, + "The Underlying Network is updating for APN (+" + + apn + + "). " + + paraString); + config.getIkeSession().setNetwork(mDefaultNetwork); + config.setIkeSessionState( + IkeSessionState.IKE_MOBILITY_IN_PROGRESS); + }); + mIkeSessionNetwork = mDefaultNetwork; + } } break; @@ -1577,30 +1624,12 @@ public class EpdgTunnelManager { tunnelConfig = mApnNameToTunnelConfig.get(apnName); if (tunnelConfig.getIface() == null) { - if (mLocalAddresses == null - || mLocalAddresses.size() == 0 - || ipSecManager == null) { - Log.e(TAG, "No local addresses found."); - closeIkeSession( - apnName, new IwlanError(IwlanError.TUNNEL_TRANSFORM_FAILED)); - return; - } - try { - if (mEpdgAddress instanceof Inet4Address - && !IwlanHelper.hasIpv4Address(mLocalAddresses)) { - mLocalAddresses = - IwlanHelper.getStackedAddressesForNetwork( - mNetwork, mContext); - } - InetAddress localAddress = - (mEpdgAddress instanceof Inet4Address) - ? IwlanHelper.getIpv4Address(mLocalAddresses) - : IwlanHelper.getIpv6Address(mLocalAddresses); - Log.d(TAG, "Local address = " + localAddress); tunnelConfig.setIface( ipSecManager.createIpSecTunnelInterface( - localAddress, mEpdgAddress, mNetwork)); + DUMMY_ADDR /* unused */, + DUMMY_ADDR /* unused */, + mDefaultNetwork)); } catch (IpSecManager.ResourceUnavailableException | IOException e) { Log.e(TAG, "Failed to create tunnel interface. " + e); closeIkeSession( @@ -1622,8 +1651,9 @@ public class EpdgTunnelManager { closeIkeSession( apnName, new IwlanError(IwlanError.TUNNEL_TRANSFORM_FAILED)); } - if (mIkeSessionState == IkeSessionState.IKE_MOBILITY_IN_PROGRESS) { - mIkeSessionState = IkeSessionState.CHILD_SESSION_OPENED; + if (tunnelConfig.getIkeSessionState() + == IkeSessionState.IKE_MOBILITY_IN_PROGRESS) { + tunnelConfig.setIkeSessionState(IkeSessionState.CHILD_SESSION_OPENED); } break; @@ -1642,7 +1672,12 @@ public class EpdgTunnelManager { Log.d(TAG, "No tunnel callback for apn: " + apnName); return; } - tunnelConfig.setError(sessionClosedData.mIwlanError); + if (sessionClosedData.mIkeException != null) { + tunnelConfig.setError( + getErrorFromIkeException( + sessionClosedData.mIkeException, + tunnelConfig.getIkeSessionState())); + } tunnelConfig.getIkeSession().close(); break; @@ -1678,7 +1713,8 @@ public class EpdgTunnelManager { case EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED: IkeSessionConnectionInfoData ikeSessionConnectionInfoData = (IkeSessionConnectionInfoData) msg.obj; - network = ikeSessionConnectionInfoData.mIkeSessionConnectionInfo.getNetwork(); + Network network = + ikeSessionConnectionInfoData.mIkeSessionConnectionInfo.getNetwork(); apnName = ikeSessionConnectionInfoData.mApnName; ConnectivityManager connectivityManager = @@ -1798,7 +1834,7 @@ public class EpdgTunnelManager { epdgAddressOrder, setupRequest.isRoaming(), setupRequest.isEmergency(), - mNetwork, + mDefaultNetwork, mSelectorCallback); if (epdgError.getErrorType() != IwlanError.NO_ERROR) { @@ -1862,17 +1898,6 @@ public class EpdgTunnelManager { mValidEpdgInfo.incrementIndex(); } - @VisibleForTesting - void resetTunnelManagerState() { - Log.d(TAG, "resetTunnelManagerState"); - mEpdgAddress = null; - setHasConnectedToEpdg(false); - mNetwork = null; - mPendingBringUpRequests = new LinkedList<>(); - mApnNameToTunnelConfig = new ConcurrentHashMap<>(); - mLocalAddresses = null; - } - private void serviceAllPendingRequests() { while (!mPendingBringUpRequests.isEmpty()) { Log.d(TAG, "serviceAllPendingRequests"); @@ -1911,20 +1936,20 @@ public class EpdgTunnelManager { // Update Network wrapper private static final class UpdateNetworkWrapper { private final Network mNetwork; - private final String mApnName; + private final LinkProperties mLinkProperties; - private UpdateNetworkWrapper(Network network, String apnName) { + private UpdateNetworkWrapper(Network network, LinkProperties linkProperties) { mNetwork = network; - mApnName = apnName; - } - - public String getApnName() { - return mApnName; + mLinkProperties = linkProperties; } public Network getNetwork() { return mNetwork; } + + public LinkProperties getLinkProperties() { + return mLinkProperties; + } } // Tunnel request + tunnel callback @@ -2049,11 +2074,11 @@ public class EpdgTunnelManager { // Data received from IkeSessionStateMachine if either IKE session or Child session have been // closed, normally or exceptionally. private static final class SessionClosedData extends IkeEventData { - final IwlanError mIwlanError; + final IkeException mIkeException; - private SessionClosedData(String apnName, int token, IwlanError iwlanError) { + private SessionClosedData(String apnName, int token, IkeException ikeException) { super(apnName, token); - mIwlanError = iwlanError; + mIkeException = ikeException; } } @@ -2241,13 +2266,24 @@ 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)); + if (Arrays.stream(nrCarrierCaps) + .anyMatch(cap -> cap == CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA)) { + return true; + } else return false; + } + + @VisibleForTesting boolean isTunnelConfigContainExistApn(String apnName) { return mApnNameToTunnelConfig.containsKey(apnName); } @VisibleForTesting List<InetAddress> getAddressForNetwork(Network network, Context context) { - return IwlanHelper.getAddressesForNetwork(network, context); + return IwlanHelper.getAllAddressesForNetwork(network, context); } @VisibleForTesting @@ -2257,7 +2293,7 @@ public class EpdgTunnelManager { @VisibleForTesting void sendSelectionRequestComplete( - ArrayList<InetAddress> validIPList, IwlanError result, int transactionId) { + List<InetAddress> validIPList, IwlanError result, int transactionId) { mEpdgServerSelectionDuration = System.currentTimeMillis() - mEpdgServerSelectionStartTime; mEpdgServerSelectionStartTime = 0; EpdgSelectorResult epdgSelectorResult = @@ -2317,8 +2353,14 @@ public class EpdgTunnelManager { } @VisibleForTesting - void setHasConnectedToEpdg(boolean value) { - mHasConnectedToEpdg = value; + void onConnectedToEpdg(boolean hasConnected) { + mHasConnectedToEpdg = hasConnected; + if (mHasConnectedToEpdg) { + mIkeSessionNetwork = mDefaultNetwork; + } else { + mIkeSessionNetwork = null; + mEpdgAddress = null; + } } @VisibleForTesting @@ -2363,11 +2405,11 @@ public class EpdgTunnelManager { @VisibleForTesting IpPreferenceConflict isIpPreferenceConflictsWithNetwork( @CarrierConfigManager.Iwlan.EpdgAddressIpPreference int ipPreference) { - mLocalAddresses = getAddressForNetwork(mNetwork, mContext); - if (mLocalAddresses == null || mLocalAddresses.size() == 0) { - Log.e(TAG, "No local addresses available."); + List<InetAddress> localAddresses = getAddressForNetwork(mDefaultNetwork, mContext); + if (localAddresses == null || localAddresses.size() == 0) { + Log.e(TAG, "No local addresses available for Network " + mDefaultNetwork); return new IpPreferenceConflict(true, IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED); - } else if (!IwlanHelper.hasIpv6Address(mLocalAddresses) + } else if (!IwlanHelper.hasIpv6Address(localAddresses) && ipPreference == CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_ONLY) { Log.e( TAG, @@ -2376,7 +2418,7 @@ public class EpdgTunnelManager { + " conflicts with source IP type: " + EpdgSelector.PROTO_FILTER_IPV4); return new IpPreferenceConflict(true, IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED); - } else if (!IwlanHelper.hasIpv4Address(mLocalAddresses) + } else if (!IwlanHelper.hasIpv4Address(localAddresses) && ipPreference == CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_ONLY) { // b/209938719 allows Iwlan to support VoWiFi for IPv4 ePDG server while on IPv6 WiFi. // Iwlan will receive a IPv4 address which is embedded in stacked IPv6 address. By using @@ -2397,6 +2439,7 @@ public class EpdgTunnelManager { public void dump(PrintWriter pw) { pw.println("---- EpdgTunnelManager ----"); pw.println("mHasConnectedToEpdg: " + mHasConnectedToEpdg); + pw.println("mIkeSessionNetwork: " + mIkeSessionNetwork); if (mEpdgAddress != null) { pw.println("mEpdgAddress: " + mEpdgAddress); } diff --git a/src/com/google/android/iwlan/epdg/SrvDnsResolver.java b/src/com/google/android/iwlan/epdg/SrvDnsResolver.java index 1ae9cba..192fbb8 100644 --- a/src/com/google/android/iwlan/epdg/SrvDnsResolver.java +++ b/src/com/google/android/iwlan/epdg/SrvDnsResolver.java @@ -36,6 +36,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -146,7 +147,7 @@ final class SrvDnsResolver { // Parses the Answers section of a DnsPacket to construct and return a mapping // of Domain Name strings to their corresponding SRV record. public @NonNull Map<String, SrvRecord> parseSrvRecords() throws ParseException { - final HashMap<String, SrvRecord> targetNameToSrvRecord = new HashMap<>(); + final HashMap<String, SrvRecord> targetNameToSrvRecord = new LinkedHashMap<>(); if (mHeader.getRecordCount(ANSECTION) == 0) return targetNameToSrvRecord; for (final DnsRecord ansSec : mRecords[ANSECTION]) { diff --git a/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java b/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java index 4086c22..d1ed9dd 100644 --- a/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java +++ b/src/com/google/android/iwlan/epdg/TunnelSetupRequest.java @@ -29,8 +29,6 @@ public abstract class TunnelSetupRequest { abstract int apnIpProtocol(); - abstract Network network(); - abstract Optional<InetAddress> srcIpv4Address(); abstract Optional<InetAddress> srcIpv6Address(); @@ -58,8 +56,6 @@ public abstract class TunnelSetupRequest { public abstract Builder setApnIpProtocol(int protocol); - public abstract Builder setNetwork(Network network); - public Builder setSrcIpv4Address(InetAddress srcIpv4Address) { return setSrcIpv4Address(Optional.ofNullable(srcIpv4Address)); } diff --git a/src/com/google/android/iwlan/proto/MetricsAtom.java b/src/com/google/android/iwlan/proto/MetricsAtom.java index 33b0fcd..7ecf464 100644 --- a/src/com/google/android/iwlan/proto/MetricsAtom.java +++ b/src/com/google/android/iwlan/proto/MetricsAtom.java @@ -16,6 +16,10 @@ package com.google.android.iwlan.proto; +import android.net.ipsec.ike.exceptions.IkeIOException; +import android.net.ipsec.ike.exceptions.IkeInternalException; + +import com.google.android.iwlan.IwlanError; import com.google.android.iwlan.IwlanStatsLog; public class MetricsAtom { @@ -37,6 +41,8 @@ public class MetricsAtom { private int mHandoverFailureMode; private int mRetryDurationMillis; private int mWifiSignalValue; + private String mIwlanErrorWrappedClassname; + private String mIwlanErrorWrappedStackFirstFrame; public void setMessageId(int messageId) { this.mMessageId = messageId; @@ -110,6 +116,36 @@ public class MetricsAtom { this.mWifiSignalValue = wifiSignalValue; } + public void setIwlanErrorWrappedClassnameAndStack(IwlanError iwlanError) { + Throwable iwlanErrorWrapped = iwlanError.getException(); + if (iwlanErrorWrapped instanceof IkeInternalException + || iwlanErrorWrapped instanceof IkeIOException) { + iwlanErrorWrapped = iwlanErrorWrapped.getCause(); + } + + if (iwlanErrorWrapped == null) { + this.mIwlanErrorWrappedClassname = null; + this.mIwlanErrorWrappedStackFirstFrame = null; + return; + } + + this.mIwlanErrorWrappedClassname = iwlanErrorWrapped.getClass().getCanonicalName(); + + StackTraceElement[] iwlanErrorWrappedStackTraceElements = iwlanErrorWrapped.getStackTrace(); + this.mIwlanErrorWrappedStackFirstFrame = + iwlanErrorWrappedStackTraceElements.length != 0 + ? iwlanErrorWrappedStackTraceElements[0].toString() + : null; + } + + public String getIwlanErrorWrappedClassname() { + return mIwlanErrorWrappedClassname; + } + + public String getIwlanErrorWrappedStackFirstFrame() { + return mIwlanErrorWrappedStackFirstFrame; + } + public void sendMetricsData() { if (mMessageId == IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED) { IwlanStatsLog.write( @@ -129,7 +165,9 @@ public class MetricsAtom { mIkeTunnelEstablishmentDurationMillis, mTunnelState, mHandoverFailureMode, - mRetryDurationMillis); + mRetryDurationMillis, + mIwlanErrorWrappedClassname, + mIwlanErrorWrappedStackFirstFrame); return; } else if (mMessageId == IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED) { IwlanStatsLog.write( diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java index 0895f09..1c15e41 100644 --- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java +++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java @@ -197,6 +197,10 @@ public class ErrorPolicyManagerTest { return buildIwlanIkeProtocolError(IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND); } + private static IwlanError buildIwlanIkeInternalAddressFailure() { + return buildIwlanIkeProtocolError(IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE); + } + @Test public void testValidCarrierConfig() throws Exception { String apn = "ims"; @@ -275,7 +279,7 @@ public class ErrorPolicyManagerTest { time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(10, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); - assertEquals(10, time); + assertEquals(20, time); } @Test @@ -311,6 +315,10 @@ public class ErrorPolicyManagerTest { // parsing (or lack of explicit carrier-defined policy). IwlanError iwlanError = buildIwlanIkeAuthFailedError(); long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(5, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(10, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(10, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(20, time); @@ -318,13 +326,13 @@ public class ErrorPolicyManagerTest { assertEquals(40, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(80, time); - time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); - assertEquals(160, time); - time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); - assertEquals(86400, time); iwlanError = buildIwlanIkeProtocolError(9002); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(5, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(10, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(10, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(20, time); @@ -332,10 +340,16 @@ public class ErrorPolicyManagerTest { assertEquals(40, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); assertEquals(80, time); + + iwlanError = buildIwlanIkeInternalAddressFailure(); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); - assertEquals(160, time); + assertEquals(0, time); time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); - assertEquals(86400, time); + assertEquals(0, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(0, time); + time = mErrorPolicyManager.reportIwlanError(apn, iwlanError); + assertEquals(10, time); } @Test diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java index 2646c16..c4549df 100644 --- a/test/com/google/android/iwlan/IwlanDataServiceTest.java +++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java @@ -17,12 +17,14 @@ 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 com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -31,9 +33,12 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -43,11 +48,13 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; +import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.vcn.VcnTransportInfo; import android.os.test.TestLooper; import android.telephony.AccessNetworkConstants.AccessNetworkType; @@ -67,12 +74,12 @@ import android.telephony.ims.ImsMmTelManager; import com.google.android.iwlan.IwlanDataService.IwlanDataServiceProvider; import com.google.android.iwlan.IwlanDataService.IwlanDataServiceProvider.IwlanTunnelCallback; import com.google.android.iwlan.IwlanDataService.IwlanDataServiceProvider.TunnelState; -import com.google.android.iwlan.IwlanDataService.IwlanNetworkMonitorCallback; 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.TunnelLinkPropertiesTest; import com.google.android.iwlan.epdg.TunnelSetupRequest; +import com.google.android.iwlan.proto.MetricsAtom; import org.junit.After; import org.junit.Before; @@ -83,11 +90,13 @@ import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import java.lang.reflect.Method; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.LongSummaryStatistics; @@ -97,6 +106,7 @@ import java.util.concurrent.TimeUnit; public class IwlanDataServiceTest { private static final int DEFAULT_SLOT_INDEX = 0; private static final int DEFAULT_SUB_INDEX = 0; + private static final int INVALID_SUB_INDEX = -1; private static final int LINK_MTU = 1280; private static final String TEST_APN_NAME = "ims"; private static final String IP_ADDRESS = "192.0.2.1"; @@ -120,21 +130,23 @@ public class IwlanDataServiceTest { @Mock private ImsMmTelManager mMockImsMmTelManager; @Mock private TelephonyManager mMockTelephonyManager; @Mock private EpdgSelector mMockEpdgSelector; - @Mock private LinkProperties mMockLinkProperties; @Mock private LinkAddress mMockIPv4LinkAddress; @Mock private LinkAddress mMockIPv6LinkAddress; @Mock private Inet4Address mMockInet4Address; @Mock private Inet6Address mMockInet6Address; + MockitoSession mStaticMockSession; + private LinkProperties mLinkProperties; private List<DataCallResponse> mResultDataCallList; private @DataServiceCallback.ResultCode int mResultCode; private CountDownLatch latch; private IwlanDataService mIwlanDataService; - private IwlanDataServiceProvider mIwlanDataServiceProvider; private IwlanDataServiceProvider mSpyIwlanDataServiceProvider; private TestLooper mTestLooper = new TestLooper(); - private long mMockedCalendarTime = 0L; + private long mMockedCalendarTime; + private ArgumentCaptor<NetworkCallback> mNetworkCallbackCaptor = + ArgumentCaptor.forClass(NetworkCallback.class); private final class IwlanDataServiceCallback extends IDataServiceCallback.Stub { @@ -189,6 +201,7 @@ public class IwlanDataServiceTest { mStaticMockSession = mockitoSession() .mockStatic(EpdgSelector.class) + .mockStatic(EpdgTunnelManager.class) .mockStatic(ErrorPolicyManager.class) .mockStatic(IwlanBroadcastReceiver.class) .mockStatic(SubscriptionManager.class) @@ -201,6 +214,12 @@ public class IwlanDataServiceTest { when(mMockContext.getSystemService(eq(SubscriptionManager.class))) .thenReturn(mMockSubscriptionManager); + doNothing() + .when(mMockConnectivityManager) + .registerSystemDefaultNetworkCallback(mNetworkCallbackCaptor.capture(), any()); + + when(EpdgTunnelManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX)) + .thenReturn(mMockEpdgTunnelManager); when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt())) .thenReturn(mMockSubscriptionInfo); when(mMockSubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX); @@ -234,45 +253,73 @@ public class IwlanDataServiceTest { when(mMockIPv6LinkAddress.getAddress()).thenReturn(mMockInet6Address); mIwlanDataService = spy(new IwlanDataService()); + // Injects the test looper into the IwlanDataServiceHandler doReturn(mTestLooper.getLooper()).when(mIwlanDataService).getLooper(); mIwlanDataService.setAppContext(mMockContext); - mIwlanDataServiceProvider = - (IwlanDataServiceProvider) - mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX); + mSpyIwlanDataServiceProvider = + spy( + (IwlanDataServiceProvider) + mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX)); mTestLooper.dispatchAll(); - mSpyIwlanDataServiceProvider = spy(mIwlanDataServiceProvider); + + when(Calendar.getInstance().getTime()).thenAnswer(i -> mMockedCalendarTime); + + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName("wlan0"); + mLinkProperties.addLinkAddress(mMockIPv4LinkAddress); + + when(mMockConnectivityManager.getLinkProperties(eq(mMockNetwork))) + .thenReturn(mLinkProperties); } @After public void cleanUp() throws Exception { mStaticMockSession.finishMocking(); - mIwlanDataServiceProvider.close(); + mSpyIwlanDataServiceProvider.close(); mTestLooper.dispatchAll(); if (mIwlanDataService != null) { mIwlanDataService.onDestroy(); } } - private void verifyNetworkConnected(int transportType) { - NetworkCapabilities mockNetworkCapabilities = mock(NetworkCapabilities.class); + public Network createMockNetwork(LinkProperties linkProperties) { + Network network = mock(Network.class); + when(mMockConnectivityManager.getLinkProperties(eq(network))).thenReturn(linkProperties); + return network; + } + + private NetworkCallback getNetworkMonitorCallback() { + return mNetworkCallbackCaptor.getValue(); + } - when(mockNetworkCapabilities.hasTransport(anyInt())).thenReturn(false); - when(mockNetworkCapabilities.hasTransport(eq(transportType))).thenReturn(true); + private void onSystemDefaultNetworkConnected( + Network network, LinkProperties linkProperties, int transportType, int subId) { + NetworkCapabilities nc = + prepareNetworkCapabilitiesForTest( + transportType, + subId /* unused if transportType is TRANSPORT_WIFI */, + false /* isVcn */); + NetworkCallback networkMonitorCallback = getNetworkMonitorCallback(); + networkMonitorCallback.onCapabilitiesChanged(network, nc); + networkMonitorCallback.onLinkPropertiesChanged(network, linkProperties); + mTestLooper.dispatchAll(); + } - IwlanNetworkMonitorCallback networkMonitorCallback = - mIwlanDataService.getNetworkMonitorCallback(); - networkMonitorCallback.onCapabilitiesChanged(mMockNetwork, mockNetworkCapabilities); + private void onSystemDefaultNetworkConnected(int transportType) { + Network newNetwork = createMockNetwork(mLinkProperties); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, transportType, DEFAULT_SUB_INDEX); } - private void verifyNetworkLost() { - IwlanNetworkMonitorCallback networkMonitorCallback = - mIwlanDataService.getNetworkMonitorCallback(); + private void onSystemDefaultNetworkLost() { + NetworkCallback networkMonitorCallback = getNetworkMonitorCallback(); networkMonitorCallback.onLost(mMockNetwork); + mTestLooper.dispatchAll(); } @Test - public void testWifionConnected() { - verifyNetworkConnected(TRANSPORT_WIFI); + public void testWifiOnConnected() { + onSystemDefaultNetworkConnected(TRANSPORT_WIFI); assertTrue( mIwlanDataService.isNetworkConnected( false /* isActiveDataOnOtherSub */, false /* isCstEnabled */)); @@ -283,7 +330,7 @@ public class IwlanDataServiceTest { when(mMockIwlanDataServiceProvider.getSlotIndex()).thenReturn(DEFAULT_SLOT_INDEX + 1); mIwlanDataService.addIwlanDataServiceProvider(mMockIwlanDataServiceProvider); - verifyNetworkLost(); + onSystemDefaultNetworkLost(); assertFalse( mIwlanDataService.isNetworkConnected( false /* isActiveDataOnOtherSub */, false /* isCstEnabled */)); @@ -293,12 +340,115 @@ public class IwlanDataServiceTest { } @Test - public void testNetworkNotConnectedWithCellularAndCrossSimDisabled() + public void testWifiOnReconnected() { + Network newNetwork = createMockNetwork(mLinkProperties); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + verify(mMockEpdgTunnelManager, times(1)).updateNetwork(eq(newNetwork), eq(mLinkProperties)); + + onSystemDefaultNetworkLost(); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + verify(mMockEpdgTunnelManager, times(2)).updateNetwork(eq(newNetwork), eq(mLinkProperties)); + } + + @Test + public void testOnLinkPropertiesChangedForConnectedNetwork() { + NetworkCallback networkCallback = getNetworkMonitorCallback(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + + clearInvocations(mMockEpdgTunnelManager); + + LinkProperties newLinkProperties = new LinkProperties(mLinkProperties); + newLinkProperties.setInterfaceName("wlan0"); + newLinkProperties.addLinkAddress(mMockIPv6LinkAddress); + + networkCallback.onLinkPropertiesChanged(mMockNetwork, newLinkProperties); + verify(mMockEpdgTunnelManager, times(1)) + .updateNetwork(eq(mMockNetwork), eq(newLinkProperties)); + } + + @Test + public void testOnLinkPropertiesChangedForNonConnectedNetwork() { + NetworkCallback networkCallback = getNetworkMonitorCallback(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + + clearInvocations(mMockEpdgTunnelManager); + + LinkProperties newLinkProperties = new LinkProperties(); + newLinkProperties.setInterfaceName("wlan0"); + newLinkProperties.addLinkAddress(mMockIPv6LinkAddress); + Network newNetwork = createMockNetwork(newLinkProperties); + + networkCallback.onLinkPropertiesChanged(newNetwork, newLinkProperties); + verify(mMockEpdgTunnelManager, never()) + .updateNetwork(eq(newNetwork), any(LinkProperties.class)); + } + + @Test + public void testOnLinkPropertiesChangedWithClatInstalled() throws Exception { + NetworkCallback networkCallback = getNetworkMonitorCallback(); + mLinkProperties.setLinkAddresses( + new ArrayList<>(Collections.singletonList(mMockIPv6LinkAddress))); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + + clearInvocations(mMockEpdgTunnelManager); + + // LinkProperties#addStackedLink() is marked with @UnsupportedAppUsage + LinkProperties newLinkProperties = new LinkProperties(mLinkProperties); + newLinkProperties.setInterfaceName("wlan0"); + LinkProperties stackedLink = new LinkProperties(); + stackedLink.setInterfaceName("v4-wlan0"); + stackedLink.addLinkAddress(mMockIPv4LinkAddress); + Class<?>[] parameterTypes = new Class<?>[] {LinkProperties.class}; + Object[] args = new Object[] {stackedLink}; + callUnsupportedAppUsageMethod(newLinkProperties, "addStackedLink", parameterTypes, args); + assertNotEquals(mLinkProperties, newLinkProperties); + + networkCallback.onLinkPropertiesChanged(mMockNetwork, newLinkProperties); + verify(mMockEpdgTunnelManager, times(1)) + .updateNetwork(eq(mMockNetwork), eq(newLinkProperties)); + } + + @Test + public void testOnLinkPropertiesChangedForBringingUpIkeSession() { + DataProfile dp = buildImsDataProfile(); + + NetworkCallback networkCallback = getNetworkMonitorCallback(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + + clearInvocations(mMockEpdgTunnelManager); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_IN_BRINGUP, + null, /* linkProperties */ + false /* isHandover */, + 1 /* pduSessionId */, + true /* isImsOrEmergency */); + + LinkProperties newLinkProperties = new LinkProperties(mLinkProperties); + newLinkProperties.setInterfaceName("wlan0"); + newLinkProperties.addLinkAddress(mMockIPv6LinkAddress); + + networkCallback.onLinkPropertiesChanged(mMockNetwork, newLinkProperties); + verify(mMockEpdgTunnelManager, times(1)) + .updateNetwork(eq(mMockNetwork), eq(newLinkProperties)); + verify(mMockEpdgTunnelManager, never()).closeTunnel(any(), anyBoolean(), any(), any()); + } + + @Test + public void testNetworkNotConnectedWithCellularOnSameSubAndCrossSimEnabled() throws InterruptedException { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); boolean isActiveDataOnOtherSub = mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX); @@ -310,11 +460,12 @@ public class IwlanDataServiceTest { } @Test - public void testCrossSimNetworkConnectedWithTelephonyNetwork() throws InterruptedException { + public void testCrossSimNetworkConnectedWithCellularOnDifferentSub() + throws InterruptedException { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); boolean isActiveDataOnOtherSub = mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX); @@ -326,11 +477,12 @@ public class IwlanDataServiceTest { } @Test - public void testCrossSimNetworkConnectedWithVcn() throws InterruptedException { + public void testCrossSimNetworkConnectedWithVcnCellularOnDifferentSub() + throws InterruptedException { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, true /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); boolean isActiveDataOnOtherSub = mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX); @@ -342,6 +494,72 @@ public class IwlanDataServiceTest { } @Test + public void testOnCrossSimCallingEnable_doNotUpdateTunnelManagerIfCellularDataOnSameSub() + throws Exception { + when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true); + + Network newNetwork = createMockNetwork(mLinkProperties); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX); + + mIwlanDataService + .mIwlanDataServiceHandler + .obtainMessage( + IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT, + DEFAULT_SLOT_INDEX, + 0 /* unused */) + .sendToTarget(); + mTestLooper.dispatchAll(); + verify(mMockEpdgTunnelManager, never()) + .updateNetwork(eq(newNetwork), any(LinkProperties.class)); + } + + @Test + public void testOnCrossSimCallingEnable_updateTunnelManagerIfCellularDataOnDifferentSub() + throws Exception { + when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true); + + Network newNetwork = createMockNetwork(mLinkProperties); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1); + verify(mMockEpdgTunnelManager, times(1)).updateNetwork(eq(newNetwork), eq(mLinkProperties)); + + mIwlanDataService + .mIwlanDataServiceHandler + .obtainMessage( + IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT, + DEFAULT_SLOT_INDEX, + 0 /* unused */) + .sendToTarget(); + mTestLooper.dispatchAll(); + verify(mMockEpdgTunnelManager, times(2)).updateNetwork(eq(newNetwork), eq(mLinkProperties)); + } + + @Test + public void testOnCrossSimCallingEnable_doNotUpdateTunnelManagerIfNoNetwork() throws Exception { + when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true); + mIwlanDataService + .mIwlanDataServiceHandler + .obtainMessage( + IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT, + DEFAULT_SLOT_INDEX, + 0 /* unused */) + .sendToTarget(); + mTestLooper.dispatchAll(); + verify(mMockEpdgTunnelManager, never()) + .updateNetwork(any(Network.class), any(LinkProperties.class)); + } + + @Test + public void testOnEthernetConnection_doNotUpdateTunnelManager() throws Exception { + Network newNetwork = createMockNetwork(mLinkProperties); + onSystemDefaultNetworkConnected( + newNetwork, mLinkProperties, TRANSPORT_ETHERNET, DEFAULT_SUB_INDEX); + verify(mMockEpdgTunnelManager, never()) + .updateNetwork(eq(newNetwork), any(LinkProperties.class)); + } + + @Test public void testAddDuplicateDataServiceProviderThrows() throws Exception { when(mMockIwlanDataServiceProvider.getSlotIndex()).thenReturn(DEFAULT_SLOT_INDEX); assertThrows( @@ -371,7 +589,7 @@ public class IwlanDataServiceTest { IwlanDataServiceCallback callback = new IwlanDataServiceCallback("requestDataCallList"); TunnelLinkProperties mLinkProperties = TunnelLinkPropertiesTest.createTestTunnelLinkProperties(); - mIwlanDataServiceProvider.setTunnelState( + mSpyIwlanDataServiceProvider.setTunnelState( dp, new DataServiceCallback(callback), TunnelState.TUNNEL_UP, @@ -379,7 +597,7 @@ public class IwlanDataServiceTest { false, /* isHandover */ 1, /* pduSessionId */ true /* isImsOrEmergency */); - mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback)); + mSpyIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback)); mTestLooper.dispatchAll(); latch.await(1, TimeUnit.SECONDS); @@ -424,7 +642,7 @@ public class IwlanDataServiceTest { public void testRequestDataCallListEmpty() throws Exception { latch = new CountDownLatch(1); IwlanDataServiceCallback callback = new IwlanDataServiceCallback("requestDataCallList"); - mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback)); + mSpyIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback)); mTestLooper.dispatchAll(); latch.await(1, TimeUnit.SECONDS); @@ -434,7 +652,7 @@ public class IwlanDataServiceTest { @Test public void testIwlanSetupDataCallWithInvalidArg() { - mIwlanDataServiceProvider.setupDataCall( + mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.UNKNOWN, /* AccessNetworkType */ null, /* dataProfile */ false, /* isRoaming */ @@ -458,10 +676,9 @@ public class IwlanDataServiceTest { DataProfile dp = buildImsDataProfile(); /* Wifi is not connected */ - mIwlanDataService.setNetworkConnected( - false, mMockNetwork, IwlanDataService.Transport.UNSPECIFIED_NETWORK); + onSystemDefaultNetworkLost(); - mIwlanDataServiceProvider.setupDataCall( + mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ dp, /* dataProfile */ false, /* isRoaming */ @@ -483,7 +700,7 @@ public class IwlanDataServiceTest { @Test public void testIwlanDeactivateDataCallWithInvalidArg() { - mIwlanDataServiceProvider.deactivateDataCall( + mSpyIwlanDataServiceProvider.deactivateDataCall( 0, /* cid */ DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */ mMockDataServiceCallback); @@ -498,9 +715,8 @@ public class IwlanDataServiceTest { DataProfile dp = buildImsDataProfile(); /* Wifi is connected */ - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ @@ -538,9 +754,8 @@ public class IwlanDataServiceTest { DataProfile dp = buildImsDataProfile(); /* Wifi is connected */ - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ @@ -585,9 +800,7 @@ public class IwlanDataServiceTest { public void testIwlanDeactivateDataCallWithCloseTunnel() { DataProfile dp = buildImsDataProfile(); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); - - verifyNetworkConnected(TRANSPORT_WIFI); + onSystemDefaultNetworkConnected(TRANSPORT_WIFI); mSpyIwlanDataServiceProvider.setTunnelState( dp, @@ -624,9 +837,7 @@ public class IwlanDataServiceTest { public void testIwlanDeactivateDataCallAfterSuccessHandover() { DataProfile dp = buildImsDataProfile(); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); - - verifyNetworkConnected(TRANSPORT_WIFI); + onSystemDefaultNetworkConnected(TRANSPORT_WIFI); mSpyIwlanDataServiceProvider.setTunnelState( dp, @@ -995,16 +1206,11 @@ public class IwlanDataServiceTest { @Test public void testDnsPrefetching() throws Exception { - IwlanNetworkMonitorCallback mNetworkMonitorCallback = - mIwlanDataService.getNetworkMonitorCallback(); + NetworkCallback networkCallback = getNetworkMonitorCallback(); /* Wifi is connected */ - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - - List<LinkAddress> linkAddresses = new ArrayList<>(); - linkAddresses.add(mMockIPv4LinkAddress); - - when(mMockLinkProperties.getLinkAddresses()).thenReturn(linkAddresses); - mNetworkMonitorCallback.onLinkPropertiesChanged(mMockNetwork, mMockLinkProperties); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + networkCallback.onLinkPropertiesChanged(mMockNetwork, mLinkProperties); mIwlanDataService .mIwlanDataServiceHandler @@ -1023,10 +1229,12 @@ public class IwlanDataServiceTest { .sendToTarget(); mTestLooper.dispatchAll(); - linkAddresses.add(mMockIPv6LinkAddress); + LinkProperties newLinkProperties = new LinkProperties(); + newLinkProperties.setInterfaceName("wlan0"); + newLinkProperties.addLinkAddress(mMockIPv4LinkAddress); + newLinkProperties.addLinkAddress(mMockIPv6LinkAddress); - when(mMockLinkProperties.getLinkAddresses()).thenReturn(linkAddresses); - mNetworkMonitorCallback.onLinkPropertiesChanged(mMockNetwork, mMockLinkProperties); + networkCallback.onLinkPropertiesChanged(mMockNetwork, newLinkProperties); /* Prefetching will be triggered twice. 1. Network connected, CarrierConfig ready, WifiCallingSetting enabled @@ -1108,9 +1316,9 @@ public class IwlanDataServiceTest { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); - mIwlanDataServiceProvider.setupDataCall( + mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ dp, /* dataProfile */ false, /* isRoaming */ @@ -1140,7 +1348,7 @@ public class IwlanDataServiceTest { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ @@ -1173,9 +1381,7 @@ public class IwlanDataServiceTest { NetworkCapabilities nc = prepareNetworkCapabilitiesForTest( TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */); - mIwlanDataService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); - - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc); mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ @@ -1212,8 +1418,9 @@ public class IwlanDataServiceTest { public void testIwlanTunnelStatsFailureCounts() { DataProfile dp = buildImsDataProfile(); - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) .thenReturn(mMockErrorPolicyManager); @@ -1238,8 +1445,8 @@ public class IwlanDataServiceTest { when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) .thenReturn(DataFailCause.ERROR_UNSPECIFIED); - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); long count = 3L; for (int i = 0; i < count; i++) { @@ -1260,8 +1467,8 @@ public class IwlanDataServiceTest { when(calendar.getTime()).thenAnswer(i -> new Date(mMockedCalendarTime)); mSpyIwlanDataServiceProvider.setCalendar(calendar); - mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + onSystemDefaultNetworkConnected( + mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX); LongSummaryStatistics tunnelSetupSuccessStats = new LongSummaryStatistics(); LongSummaryStatistics tunnelUpStats = new LongSummaryStatistics(); @@ -1312,7 +1519,7 @@ public class IwlanDataServiceTest { @Test public void testIwlanDataServiceHandlerOnUnbind() { DataProfile dp = buildImsDataProfile(); - doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager(); + mSpyIwlanDataServiceProvider.setTunnelState( dp, mMockDataServiceCallback, @@ -1357,6 +1564,122 @@ public class IwlanDataServiceTest { mTestLooper.dispatchAll(); } + @Test + public void testBackToBackOnBindAndOnUnbindDoesNotThrow() { + mIwlanDataService.onBind(null); + mIwlanDataService.onUnbind(null); + } + + @Test + public void testMetricsWhenTunnelClosedWithWrappedException() { + DataProfile dp = buildImsDataProfile(); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_IN_BRINGUP, + null, /* linkProperties */ + false /* isHandover */, + 1 /* pduSessionId */, + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.setMetricsAtom( + TEST_APN_NAME, + 64, // type IMS + true, + 13, // LTE + false, + true, + 1 // Transport Wi-Fi + ); + + MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME); + assertNotNull(metricsAtom); + + String exceptionMessage = "Some exception message"; + Exception mockException = spy(new IllegalStateException(exceptionMessage)); + String firstDeclaringClassName = "test.test.TestClass"; + String firstMethodName = "someMethod"; + String firstFileName = "TestClass.java"; + int firstLineNumber = 12345; + StackTraceElement[] stackTraceElements = { + new StackTraceElement( + firstDeclaringClassName, firstMethodName, firstFileName, firstLineNumber), + new StackTraceElement("test", "test", "test.java", 123) + }; + doReturn(stackTraceElements).when(mockException).getStackTrace(); + + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) + .thenReturn(mMockErrorPolicyManager); + when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) + .thenReturn(DataFailCause.ERROR_UNSPECIFIED); + + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed(TEST_APN_NAME, new IwlanError(new IkeInternalException(mockException))); + + mTestLooper.dispatchAll(); + + var expectedStackFirstFrame = + firstDeclaringClassName + + "." + + firstMethodName + + "(" + + firstFileName + + ":" + + firstLineNumber + + ")"; + + assertEquals( + mockException.getClass().getCanonicalName(), + metricsAtom.getIwlanErrorWrappedClassname()); + + assertEquals(expectedStackFirstFrame, metricsAtom.getIwlanErrorWrappedStackFirstFrame()); + } + + @Test + public void testMetricsWhenTunnelClosedWithoutWrappedException() { + DataProfile dp = buildImsDataProfile(); + + mSpyIwlanDataServiceProvider.setTunnelState( + dp, + mMockDataServiceCallback, + TunnelState.TUNNEL_IN_BRINGUP, + null, /* linkProperties */ + false /* isHandover */, + 1 /* pduSessionId */, + true /* isImsOrEmergency */); + + mSpyIwlanDataServiceProvider.setMetricsAtom( + TEST_APN_NAME, + 64, // type IMS + true, + 13, // LTE + false, + true, + 1 // Transport Wi-Fi + ); + + MetricsAtom metricsAtom = mSpyIwlanDataServiceProvider.getMetricsAtomByApn(TEST_APN_NAME); + assertNotNull(metricsAtom); + + when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX))) + .thenReturn(mMockErrorPolicyManager); + when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME))) + .thenReturn(DataFailCause.ERROR_UNSPECIFIED); + + mSpyIwlanDataServiceProvider + .getIwlanTunnelCallback() + .onClosed( + TEST_APN_NAME, + new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED)); + + mTestLooper.dispatchAll(); + + assertEquals(null, metricsAtom.getIwlanErrorWrappedClassname()); + assertEquals(null, metricsAtom.getIwlanErrorWrappedStackFirstFrame()); + } + private void mockTunnelSetupFail(DataProfile dp) { mSpyIwlanDataServiceProvider.setupDataCall( AccessNetworkType.IWLAN, /* AccessNetworkType */ @@ -1447,4 +1770,12 @@ public class IwlanDataServiceTest { verify(mMockDataServiceCallback, atLeastOnce()) .onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS)); } + + private Object callUnsupportedAppUsageMethod( + Object target, String methodName, Class<?>[] parameterTypes, Object[] args) + throws Exception { + Method method = target.getClass().getDeclaredMethod(methodName, parameterTypes); + method.setAccessible(true); + return method.invoke(target, args); + } } diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java index 4a9d5a4..3f23dab 100644 --- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java @@ -80,6 +80,8 @@ public class EpdgSelectorTest { private static final String TEST_IP_ADDRESS_3 = "127.0.0.4"; private static final String TEST_IP_ADDRESS_4 = "127.0.0.5"; private static final String TEST_IP_ADDRESS_5 = "127.0.0.6"; + private static final String TEST_IP_ADDRESS_6 = "127.0.0.7"; + private static final String TEST_IP_ADDRESS_7 = "127.0.0.8"; private static final String TEST_IPV6_ADDRESS = "0000:0000:0000:0000:0000:0000:0000:0001"; private static int testPcoIdIPv6 = 0xFF01; @@ -122,7 +124,7 @@ public class EpdgSelectorTest { when(ErrorPolicyManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX)) .thenReturn(mMockErrorPolicyManager); - mEpdgSelector = new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX); + mEpdgSelector = spy(new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX)); when(mMockContext.getSystemService(eq(SubscriptionManager.class))) .thenReturn(mMockSubscriptionManager); @@ -134,6 +136,8 @@ public class EpdgSelectorTest { when(mMockSubscriptionInfo.getMncString()).thenReturn("120"); + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("311120"); + when(mMockContext.getSystemService(eq(TelephonyManager.class))) .thenReturn(mMockTelephonyManager); @@ -159,8 +163,6 @@ public class EpdgSelectorTest { .thenReturn(mMockCarrierConfigManager); when(mMockCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mTestBundle); - lenient().when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); - mFakeDns = new FakeDns(); mFakeDns.startMocking(); } @@ -173,6 +175,10 @@ public class EpdgSelectorTest { @Test public void testStaticMethodPass() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + // Set DnsResolver query mock final String testStaticAddress = "epdg.epc.mnc088.mcc888.pub.3gppnetwork.org"; mFakeDns.setAnswer(testStaticAddress, new String[] {TEST_IP_ADDRESS}, TYPE_A); @@ -189,12 +195,32 @@ public class EpdgSelectorTest { InetAddress expectedAddress = InetAddress.getByName(TEST_IP_ADDRESS); - assertEquals(testInetAddresses.size(), 1); - assertEquals(testInetAddresses.get(0), expectedAddress); + assertEquals(1, testInetAddresses.size()); + assertEquals(expectedAddress, testInetAddresses.get(0)); + } + + @Test + public void testStaticMethodDirectIpAddress_noDnsResolution() throws Exception { + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC}); + // Carrier config directly contains the ePDG IP address. + mTestBundle.putString( + CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, TEST_IP_ADDRESS); + + ArrayList<InetAddress> testInetAddresses = + getValidatedServerListWithDefaultParams(false /*isEmergency*/); + + assertEquals(1, testInetAddresses.size()); + assertEquals(InetAddresses.parseNumericAddress(TEST_IP_ADDRESS), testInetAddresses.get(0)); } @Test public void testRoamStaticMethodPass() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + // Set DnsResolver query mock final String testRoamStaticAddress = "epdg.epc.mnc088.mcc888.pub.3gppnetwork.org"; mFakeDns.setAnswer(testRoamStaticAddress, new String[] {TEST_IP_ADDRESS}, TYPE_A); @@ -212,8 +238,8 @@ public class EpdgSelectorTest { InetAddress expectedAddress = InetAddress.getByName(TEST_IP_ADDRESS); - assertEquals(testInetAddresses.size(), 1); - assertEquals(testInetAddresses.get(0), expectedAddress); + assertEquals(1, testInetAddresses.size()); + assertEquals(expectedAddress, testInetAddresses.get(0)); } @Test @@ -228,65 +254,275 @@ public class EpdgSelectorTest { @Test public void testPlmnResolutionMethodWithNoPlmnInCarrierConfig() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + // setUp() fills default values for mcc-mnc - String expectedFqdn1 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; - String expectedFqdn2 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String expectedFqdnFromImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromEhplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; - mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); - mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false /*isEmergency*/); - assertEquals(testInetAddresses.size(), 2); + assertEquals(2, testInetAddresses.size()); assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IP_ADDRESS_1))); assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IP_ADDRESS_2))); } private void testPlmnResolutionMethod(boolean isEmergency) throws Exception { - String expectedFqdnFromHplmn = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; - String expectedFqdnFromEHplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; - String expectedFqdnFromConfig = "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"; + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + String expectedFqdnFromImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromRplmn = "epdg.epc.mnc121.mcc311.pub.3gppnetwork.org"; + String expectedFqdnFromEhplmn = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String excludedFqdnFromConfig = "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("311121"); mTestBundle.putIntArray( CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); mTestBundle.putStringArray( CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, - new String[] {"310-480", "300-120", "311-120"}); + new String[] {"310-480", "300-120", "311-120", "311-121"}); - mFakeDns.setAnswer(expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A); - mFakeDns.setAnswer(expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); - mFakeDns.setAnswer(expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(excludedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer("sos." + expectedFqdnFromImsi, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + "sos." + expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A); + "sos." + excludedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, TYPE_A); + mFakeDns.setAnswer(expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_6}, TYPE_A); mFakeDns.setAnswer( - "sos." + expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, TYPE_A); + "sos." + expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_7}, TYPE_A); ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(isEmergency); if (isEmergency) { assertEquals(6, testInetAddresses.size()); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(0)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_4), testInetAddresses.get(2)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(3)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_5), testInetAddresses.get(4)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(5)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_7), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_6), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(3)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_4), testInetAddresses.get(4)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(5)); } else { assertEquals(3, testInetAddresses.size()); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(1)); - assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_6), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(2)); } } @Test + public void testPlmnResolutionMethodWithDuplicatedImsiAndEhplmn() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2AndImsi = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; + String fqdnFromEhplmn3 = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn4 = "epdg.epc.mnc123.mcc300.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300121"); + ehplmnList.add("300122"); + ehplmnList.add("300123"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] { + CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL, + }); + + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2AndImsi, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn4, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(4, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_3), testInetAddresses.get(3)); + } + + @Test + public void testPlmnResolutionMethodWithInvalidLengthPlmns() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + when(mMockSubscriptionInfo.getMccString()).thenReturn("31"); + when(mMockSubscriptionInfo.getMncString()).thenReturn("12"); + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300"); + ehplmnList.add("3001"); + ehplmnList.add("3"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] { + CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL, + }); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(0, testInetAddresses.size()); + } + + @Test + public void testPlmnResolutionMethodWithInvalidCharacterPlmns() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + when(mMockSubscriptionInfo.getMccString()).thenReturn("a b"); + when(mMockSubscriptionInfo.getMncString()).thenReturn("!@#"); + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("a cde#"); + ehplmnList.add("abcdef"); + ehplmnList.add("1 23456"); + ehplmnList.add("1 2345"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] { + CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL, + }); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(0, testInetAddresses.size()); + } + + @Test + public void testPlmnResolutionMethodWithEmptyPlmns() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + when(mMockSubscriptionInfo.getMccString()).thenReturn(null); + when(mMockSubscriptionInfo.getMncString()).thenReturn(null); + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn(""); + ehplmnList.add(""); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] { + CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN, + CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL, + }); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(0, testInetAddresses.size()); + } + + @Test + public void testPlmnResolutionMethodWithFirstEhplmn() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2 = "epdg.epc.mnc121.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn3 = "epdg.epc.mnc122.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn4 = "epdg.epc.mnc123.mcc300.pub.3gppnetwork.org"; + + ehplmnList.add("300121"); + ehplmnList.add("300122"); + ehplmnList.add("300123"); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_FIRST}); + + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn4, new String[] {TEST_IP_ADDRESS_3}, TYPE_A); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(1, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); + } + + @Test + public void testPlmnResolutionMethodWithRplmn() throws Exception { + 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"; + String fqdnFromEhplmn1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; + String fqdnFromEhplmn2 = "epdg.epc.mnc121.mcc300.pub.3gppnetwork.org"; + + when(mMockTelephonyManager.getNetworkOperator()).thenReturn("300122"); + ehplmnList.add("300121"); + + mTestBundle.putStringArray( + CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, + new String[] {"310-480", "300-122", "300-121"}); + + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN}); + mTestBundle.putIntArray( + CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, + new int[] {CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN}); + + mFakeDns.setAnswer(fqdnFromRplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A); + mFakeDns.setAnswer(fqdnFromEhplmn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A); + + ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false); + + assertEquals(1, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); + } + + @Test public void testCarrierConfigStaticAddressList() throws Exception { - // Set Network.getAllByName mock + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + + // Set DnsResolver query mock final String addr1 = "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr3 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; @@ -306,10 +542,10 @@ public class EpdgSelectorTest { ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false /*isEmergency*/); - assertEquals(testInetAddresses.size(), 3); - assertEquals(testInetAddresses.get(0), InetAddress.getByName(TEST_IP_ADDRESS_1)); - assertEquals(testInetAddresses.get(1), InetAddress.getByName(TEST_IP_ADDRESS_2)); - assertEquals(testInetAddresses.get(2), InetAddress.getByName(TEST_IP_ADDRESS)); + assertEquals(3, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(2)); } private ArrayList<InetAddress> getValidatedServerListWithDefaultParams(boolean isEmergency) @@ -336,7 +572,7 @@ public class EpdgSelectorTest { new EpdgSelector.EpdgSelectorCallback() { @Override public void onServerListChanged( - int transactionId, ArrayList<InetAddress> validIPList) { + int transactionId, List<InetAddress> validIPList) { assertEquals(transactionId, 1234); for (InetAddress mInetAddress : validIPList) { @@ -389,7 +625,7 @@ public class EpdgSelectorTest { ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(false /* isEmergency */); - assertEquals(testInetAddresses.size(), 2); + assertEquals(2, testInetAddresses.size()); assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IP_ADDRESS))); assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IPV6_ADDRESS))); } @@ -410,6 +646,8 @@ public class EpdgSelectorTest { } private void testCellularResolutionMethod(boolean isEmergency) throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + int testMcc = 311; int testMnc = 120; String testMccString = "311"; @@ -461,10 +699,10 @@ public class EpdgSelectorTest { ArrayList<InetAddress> testInetAddresses = getValidatedServerListWithDefaultParams(isEmergency); - assertEquals(testInetAddresses.size(), 3); - assertEquals(testInetAddresses.get(0), InetAddress.getByName(TEST_IP_ADDRESS)); - assertEquals(testInetAddresses.get(1), InetAddress.getByName(TEST_IP_ADDRESS_1)); - assertEquals(testInetAddresses.get(2), InetAddress.getByName(TEST_IP_ADDRESS_2)); + assertEquals(3, testInetAddresses.size()); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(1)); + assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2)); } private void setAnswerForCellularMethod(boolean isEmergency, int mcc, int mnc) @@ -505,6 +743,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListIpv4Preferred() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + final String addr1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; final String testStaticAddress = addr1 + "," + addr2; @@ -532,6 +774,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListIpv6Preferred() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + final String addr1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; final String testStaticAddress = addr1 + "," + addr2; @@ -559,6 +805,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListIpv4Only() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + final String addr1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; final String testStaticAddress = addr1 + "," + addr2; @@ -585,6 +835,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListIpv4OnlyCongestion() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + when(mMockErrorPolicyManager.getMostRecentDataFailCause()) .thenReturn(DataFailCause.IWLAN_CONGESTION); when(mMockErrorPolicyManager.getCurrentFqdnIndex(anyInt())).thenReturn(0); @@ -616,6 +870,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListIpv6Only() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + final String addr1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; final String testStaticAddress = addr1 + "," + addr2; @@ -642,6 +900,10 @@ public class EpdgSelectorTest { @Test public void testGetValidatedServerListSystemPreferred() throws Exception { + when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver); + doReturn(true).when(mEpdgSelector).hasIpv4Address(mMockNetwork); + doReturn(true).when(mEpdgSelector).hasIpv6Address(mMockNetwork); + final String addr1 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"; final String addr2 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"; final String addr3 = "epdg.epc.mnc120.mcc312.pub.3gppnetwork.org"; @@ -695,20 +957,20 @@ public class EpdgSelectorTest { } } - private final ArrayList<DnsEntry> mAnswers = new ArrayList<DnsEntry>(); + private final List<DnsEntry> mAnswers = new ArrayList<>(); /** Clears all DNS entries. */ private synchronized void clearAll() { mAnswers.clear(); } - /** Returns the answer for a given name and type on the given mock network. */ - private synchronized List<InetAddress> getAnswer(Object mock, String hostname, int type) { + /** Returns the answer for a given name and type. */ + private synchronized List<InetAddress> getAnswer(String hostname, int type) { return mAnswers.stream() .filter(e -> e.matches(hostname, type)) .map(answer -> answer.mAddresses) .findFirst() - .orElse(null); + .orElse(List.of()); } /** Sets the answer for a given name and type. */ @@ -729,10 +991,20 @@ public class EpdgSelectorTest { } // Regardless of the type, depends on what the responses contained in the network. - private List<InetAddress> queryAllTypes(Object mock, String hostname) { + private List<InetAddress> queryIpv4(String hostname) { + return getAnswer(hostname, TYPE_A); + } + + // Regardless of the type, depends on what the responses contained in the network. + private List<InetAddress> queryIpv6(String hostname) { + return getAnswer(hostname, TYPE_AAAA); + } + + // Regardless of the type, depends on what the responses contained in the network. + private List<InetAddress> queryAllTypes(String hostname) { List<InetAddress> answer = new ArrayList<>(); - addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_A)); - addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_AAAA)); + answer.addAll(queryIpv4(hostname)); + answer.addAll(queryIpv6(hostname)); return answer; } @@ -744,32 +1016,55 @@ public class EpdgSelectorTest { /** Starts mocking DNS queries. */ private void startMocking() throws UnknownHostException { + // 5-arg DnsResolver.query() doAnswer( invocation -> { return mockQuery( invocation, 1 /* posHostname */, + -1 /* posType */, 3 /* posExecutor */, - 5 /* posCallback */, - -1 /* posType */); + 5 /* posCallback */); }) .when(mMockDnsResolver) - .query(any(), any(), anyInt(), any(), any(), any()); + .query(any(), anyString(), anyInt(), any(), any(), any()); + + // 6-arg DnsResolver.query() with explicit query type (IPv4 or v6). + doAnswer( + invocation -> { + return mockQuery( + invocation, + 1 /* posHostname */, + 2 /* posType */, + 4 /* posExecutor */, + 6 /* posCallback */); + }) + .when(mMockDnsResolver) + .query(any(), anyString(), anyInt(), anyInt(), any(), any(), any()); } // Mocking queries on DnsResolver#query. private Answer mockQuery( InvocationOnMock invocation, int posHostname, + int posType, int posExecutor, - int posCallback, - int posType) { - String hostname = (String) invocation.getArgument(posHostname); - Executor executor = (Executor) invocation.getArgument(posExecutor); + int posCallback) { + String hostname = invocation.getArgument(posHostname); + Executor executor = invocation.getArgument(posExecutor); DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback); List<InetAddress> answer; - answer = queryAllTypes(invocation.getMock(), hostname); + switch (posType) { + case TYPE_A: + answer = queryIpv4(hostname); + break; + case TYPE_AAAA: + answer = queryIpv6(hostname); + break; + default: + answer = queryAllTypes(hostname); + } if (answer != null && answer.size() > 0) { new Handler(Looper.getMainLooper()) diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java index 8f14ff5..76ceec8 100644 --- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java +++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; @@ -104,23 +105,23 @@ public class EpdgTunnelManagerTest { public static final int DEFAULT_TOKEN = 0; private static final String EPDG_ADDRESS = "127.0.0.1"; + private static final String EPDG_ADDRESS_IPV6 = "2600:387:f:707::1"; private static final String TEST_APN_NAME = "www.xyz.com"; - private static final ArrayList<InetAddress> EXPECTED_LOCAL_ADDRESSES = - new ArrayList<>(List.of(InetAddresses.parseNumericAddress("201.1.100.10"))); - private static final ArrayList<InetAddress> EXPECTED_IPV6_LOCAL_ADDRESSES = - new ArrayList<>(List.of(InetAddresses.parseNumericAddress("2001:db8::1:2"))); - private static final ArrayList<InetAddress> EXPECTED_EPDG_ADDRESSES = - new ArrayList<>(List.of(InetAddresses.parseNumericAddress(EPDG_ADDRESS))); - private static final ArrayList<LinkAddress> EXPECTED_INTERNAL_ADDRESSES = - new ArrayList<>( - List.of( - new LinkAddress( - InetAddresses.parseNumericAddress("198.50.100.10"), 24))); - private static final ArrayList<InetAddress> EXPECTED_PCSCF_ADDRESSES = - new ArrayList<>(List.of(InetAddresses.parseNumericAddress("198.51.100.10"))); - private static final ArrayList<InetAddress> EXPECTED_DNS_ADDRESSES = - new ArrayList<>(List.of(InetAddresses.parseNumericAddress("198.50.100.10"))); + private static final List<InetAddress> EXPECTED_LOCAL_ADDRESSES = + List.of(InetAddresses.parseNumericAddress("201.1.100.10")); + private static final List<InetAddress> EXPECTED_IPV6_LOCAL_ADDRESSES = + List.of(InetAddresses.parseNumericAddress("2001:db8::1:2")); + private static final List<InetAddress> EXPECTED_EPDG_ADDRESSES = + List.of(InetAddresses.parseNumericAddress(EPDG_ADDRESS)); + private static final List<InetAddress> EXPECTED_EPDG_ADDRESSES_IPV6 = + List.of(InetAddresses.parseNumericAddress(EPDG_ADDRESS_IPV6)); + private static final List<LinkAddress> EXPECTED_INTERNAL_ADDRESSES = + List.of(new LinkAddress(InetAddresses.parseNumericAddress("198.50.100.10"), 24)); + private static final List<InetAddress> EXPECTED_PCSCF_ADDRESSES = + List.of(InetAddresses.parseNumericAddress("198.51.100.10")); + private static final List<InetAddress> EXPECTED_DNS_ADDRESSES = + List.of(InetAddresses.parseNumericAddress("198.50.100.10")); private EpdgTunnelManager mEpdgTunnelManager; @@ -134,11 +135,11 @@ public class EpdgTunnelManagerTest { private TestLooper mTestLooper = new TestLooper(); @Mock private Context mMockContext; + @Mock private Network mMockDefaultNetwork; @Mock private IwlanTunnelCallback mMockIwlanTunnelCallback; @Mock private IwlanTunnelMetricsImpl mMockIwlanTunnelMetrics; @Mock private IkeSession mMockIkeSession; @Mock private EpdgSelector mMockEpdgSelector; - @Mock private Network mMockNetwork; @Mock CarrierConfigManager mMockCarrierConfigManager; @Mock ConnectivityManager mMockConnectivityManager; @Mock SubscriptionManager mMockSubscriptionManager; @@ -177,7 +178,6 @@ public class EpdgTunnelManagerTest { doReturn(mTestLooper.getLooper()).when(mEpdgTunnelManager).getLooper(); setVariable(mEpdgTunnelManager, "mContext", mMockContext); mEpdgTunnelManager.initHandler(); - mEpdgTunnelManager.resetTunnelManagerState(); doReturn(mMockEpdgSelector).when(mEpdgTunnelManager).getEpdgSelector(); when(mEpdgTunnelManager.getIkeSessionCreator()).thenReturn(mMockIkeSessionCreator); @@ -187,7 +187,7 @@ public class EpdgTunnelManagerTest { anyInt(), anyBoolean(), anyBoolean(), - eq(mMockNetwork), + any(Network.class), any(EpdgSelector.EpdgSelectorCallback.class))) .thenReturn(new IwlanError(IwlanError.NO_ERROR)); @@ -202,16 +202,20 @@ public class EpdgTunnelManagerTest { when(mMockChildSessionConfiguration.getInternalAddresses()) .thenReturn(EXPECTED_INTERNAL_ADDRESSES); - when(mMockIpSecManager.createIpSecTunnelInterface(any(), any(), any())) + when(mMockIpSecManager.createIpSecTunnelInterface( + any(InetAddress.class), any(InetAddress.class), any(Network.class))) .thenReturn(mMockIpSecTunnelInterface); + when(mMockIpSecTunnelInterface.getInterfaceName()).thenReturn("ipsec10"); - when(mMockIpSecTunnelInterface.getInterfaceName()).thenReturn("wlan0"); - - when(mMockIkeSessionConnectionInfo.getNetwork()).thenReturn(mMockNetwork); + when(mMockIkeSessionConnectionInfo.getNetwork()).thenReturn(mMockDefaultNetwork); doReturn(EXPECTED_LOCAL_ADDRESSES) .when(mEpdgTunnelManager) .getAddressForNetwork(any(), any()); + + when(mMockLinkProperties.isReachable(any())).thenReturn(true); + mEpdgTunnelManager.updateNetwork(mMockDefaultNetwork, mMockLinkProperties); + mTestLooper.dispatchAll(); } @Test @@ -346,11 +350,12 @@ public class EpdgTunnelManagerTest { anyInt(), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } - private void setupTunnelBringup(String apnName, int transactionId) throws Exception { + private void setupTunnelBringup( + String apnName, List<InetAddress> epdgAddresses, int transactionId) throws Exception { setupMockForGetConfig(null); doReturn(null) .when(mMockIkeSessionCreator) @@ -373,12 +378,12 @@ public class EpdgTunnelManagerTest { mTestLooper.dispatchAll(); mEpdgTunnelManager.sendSelectionRequestComplete( - EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), transactionId); + epdgAddresses, new IwlanError(IwlanError.NO_ERROR), transactionId); mTestLooper.dispatchAll(); } private void setupTunnelBringup() throws Exception { - setupTunnelBringup(TEST_APN_NAME, 1 /* transactionId */); + setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES, 1 /* transactionId */); } @Test @@ -472,30 +477,25 @@ public class EpdgTunnelManagerTest { @Test public void testBringUpTunnelWithMobilityOptions() throws Exception { - doReturn(null) - .when(mMockIkeSessionCreator) + setupTunnelBringup(); + ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor = + ArgumentCaptor.forClass(IkeSessionParams.class); + verify(mMockIkeSessionCreator, atLeastOnce()) .createIkeSession( eq(mMockContext), - any(IkeSessionParams.class), + ikeSessionParamsCaptor.capture(), any(ChildSessionParams.class), any(Executor.class), any(IkeSessionCallback.class), any(ChildSessionCallback.class)); + IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue(); + assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)); + assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY)); + } - doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME)); - - boolean ret = - mEpdgTunnelManager.bringUpTunnel( - getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP), - mMockIwlanTunnelCallback, - mMockIwlanTunnelMetrics); - assertTrue(ret); - mTestLooper.dispatchAll(); - - mEpdgTunnelManager.sendSelectionRequestComplete( - EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1); - mTestLooper.dispatchAll(); - + @Test + public void testBringUpTunnelIpv6_verifyMobikeDisabled() throws Exception { + setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES_IPV6, 1); ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor = ArgumentCaptor.forClass(IkeSessionParams.class); verify(mMockIkeSessionCreator, atLeastOnce()) @@ -507,8 +507,8 @@ public class EpdgTunnelManagerTest { any(IkeSessionCallback.class), any(ChildSessionCallback.class)); IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue(); - assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)); assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY)); + assertFalse(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)); } @Test @@ -628,16 +628,6 @@ public class EpdgTunnelManagerTest { setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); - doReturn(null) .when(mMockIkeSessionCreator) .createIkeSession( @@ -696,16 +686,6 @@ public class EpdgTunnelManagerTest { setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); - doReturn(null) .when(mMockIkeSessionCreator) .createIkeSession( @@ -755,15 +735,6 @@ public class EpdgTunnelManagerTest { bundle.putInt(CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT, testDpdDelay); setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); doReturn(null) .when(mMockIkeSessionCreator) @@ -815,15 +786,6 @@ public class EpdgTunnelManagerTest { doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error)); setupMockForGetConfig(null); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); doReturn(null) .doReturn(null) @@ -874,15 +836,6 @@ public class EpdgTunnelManagerTest { doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error)); setupMockForGetConfig(null); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); doReturn(null) .doReturn(null) @@ -1000,15 +953,7 @@ public class EpdgTunnelManagerTest { } setupMockForGetConfig(null); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); + doReturn(null) .doReturn(null) .when(mMockIkeSessionCreator) @@ -1078,7 +1023,6 @@ public class EpdgTunnelManagerTest { TunnelSetupRequest ret = TunnelSetupRequest.builder() .setApnName(apnName) - .setNetwork(mMockNetwork) .setIsRoaming(false /*isRoaming*/) .setIsEmergency(false /*IsEmergency*/) .setRequestPcscf(false /*requestPcscf*/) @@ -1091,7 +1035,6 @@ public class EpdgTunnelManagerTest { private TunnelSetupRequest getHandoverTunnelSetupRequest(String apnName, int apnIpProtocol) { TunnelSetupRequest.Builder bld = TunnelSetupRequest.builder(); bld.setApnName(apnName) - .setNetwork(mMockNetwork) .setIsRoaming(false /*isRoaming*/) .setIsEmergency(false /*IsEmergency*/) .setRequestPcscf(false /*requestPcscf*/) @@ -1171,14 +1114,13 @@ public class EpdgTunnelManagerTest { 0); int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + mEpdgTunnelManager.onConnectedToEpdg(true); mEpdgTunnelManager.setEpdgAddress(InetAddresses.parseNumericAddress(EPDG_ADDRESS)); mEpdgTunnelManager.getTmIkeSessionCallback(testApnName, token).onClosed(); mTestLooper.dispatchAll(); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error)); - verify(mEpdgTunnelManager, times(2)).resetTunnelManagerState(); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1206,13 +1148,12 @@ public class EpdgTunnelManagerTest { EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1); mTestLooper.dispatchAll(); - mEpdgTunnelManager.setHasConnectedToEpdg(false); + mEpdgTunnelManager.onConnectedToEpdg(false); mEpdgTunnelManager.getTmIkeSessionCallback(testApnName, DEFAULT_TOKEN).onClosed(); mTestLooper.dispatchAll(); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error)); - verify(mEpdgTunnelManager, times(2)).resetTunnelManagerState(); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1224,9 +1165,8 @@ public class EpdgTunnelManagerTest { mMockIwlanTunnelMetrics, null, 0); - setVariable(mEpdgTunnelManager, "mLocalAddresses", EXPECTED_LOCAL_ADDRESSES); mEpdgTunnelManager.validateAndSetEpdgAddress(EXPECTED_EPDG_ADDRESSES); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + mEpdgTunnelManager.onConnectedToEpdg(true); } private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(String apnName) { @@ -1303,7 +1243,9 @@ public class EpdgTunnelManagerTest { return ikeSessionArgumentCaptors; } - private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback) { + private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback) + throws Exception { + clearInvocations(mMockIpSecManager); doReturn(0L) .when(mEpdgTunnelManager) .reportIwlanError(eq(apnName), eq(new IwlanError(IwlanError.NO_ERROR))); @@ -1318,9 +1260,12 @@ public class EpdgTunnelManagerTest { childSessionCallback.onIpSecTransformCreated( mMockedIpSecTransformOut, IpSecManager.DIRECTION_OUT); mTestLooper.dispatchAll(); + verify(mMockIpSecManager, times(1)) + .createIpSecTunnelInterface( + any(InetAddress.class), any(InetAddress.class), eq(mMockDefaultNetwork)); + childSessionCallback.onOpened(mMockChildSessionConfiguration); mTestLooper.dispatchAll(); - verify(mEpdgTunnelManager, times(1)) .reportIwlanError(eq(apnName), eq(new IwlanError(IwlanError.NO_ERROR))); verify(mMockIwlanTunnelCallback, times(1)).onOpened(eq(apnName), any()); @@ -1333,6 +1278,12 @@ public class EpdgTunnelManagerTest { setOneTunnelOpened(openedApnName); + // FIXME: Since the network from bringUpTunnel() will only be stored for the first request, + // and we are skipping the first tunnel setup procedure in this test case, it is necessary + // to set the network instance directly. + mEpdgTunnelManager.updateNetwork(mMockDefaultNetwork, mMockLinkProperties); + mTestLooper.dispatchAll(); + IkeSessionArgumentCaptors ikeSessionArgumentCaptors = verifyBringUpTunnel(toBeOpenedApnName, false /* needPendingBringUpReq */); ChildSessionCallback childSessionCallback = @@ -1375,7 +1326,7 @@ public class EpdgTunnelManagerTest { 0); int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + mEpdgTunnelManager.onConnectedToEpdg(true); mEpdgTunnelManager.setEpdgAddress(InetAddresses.parseNumericAddress(EPDG_ADDRESS)); mEpdgTunnelManager @@ -1384,7 +1335,6 @@ public class EpdgTunnelManagerTest { mTestLooper.dispatchAll(); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error)); - verify(mEpdgTunnelManager, times(2)).resetTunnelManagerState(); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1411,7 +1361,7 @@ public class EpdgTunnelManagerTest { EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1); mTestLooper.dispatchAll(); - mEpdgTunnelManager.setHasConnectedToEpdg(false); + mEpdgTunnelManager.onConnectedToEpdg(false); mEpdgTunnelManager .getTmIkeSessionCallback(testApnName, DEFAULT_TOKEN) @@ -1419,7 +1369,6 @@ public class EpdgTunnelManagerTest { mTestLooper.dispatchAll(); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), any(IwlanError.class)); - verify(mEpdgTunnelManager, times(2)).resetTunnelManagerState(); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1485,7 +1434,7 @@ public class EpdgTunnelManagerTest { .onIkeSessionConnectionInfoChanged(mMockIkeSessionConnectionInfo); mTestLooper.dispatchAll(); - verify(mMockIpSecTunnelInterface, times(1)).setUnderlyingNetwork(mMockNetwork); + verify(mMockIpSecTunnelInterface, times(1)).setUnderlyingNetwork(mMockDefaultNetwork); } @Test @@ -1545,16 +1494,6 @@ public class EpdgTunnelManagerTest { PersistableBundle bundle = new PersistableBundle(); setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); - doReturn(null) .when(mMockIkeSessionCreator) .createIkeSession( @@ -1687,7 +1626,7 @@ public class EpdgTunnelManagerTest { 0); int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + mEpdgTunnelManager.onConnectedToEpdg(true); mEpdgTunnelManager.setEpdgAddress(InetAddresses.parseNumericAddress(EPDG_ADDRESS)); mEpdgTunnelManager @@ -1696,7 +1635,6 @@ public class EpdgTunnelManagerTest { mTestLooper.dispatchAll(); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error)); - verify(mEpdgTunnelManager, times(2)).resetTunnelManagerState(); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1722,7 +1660,7 @@ public class EpdgTunnelManagerTest { mEpdgTunnelManager.sendSelectionRequestComplete(null, error, 1); mTestLooper.dispatchAll(); - mEpdgTunnelManager.setHasConnectedToEpdg(false); + mEpdgTunnelManager.onConnectedToEpdg(false); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error)); } @@ -1779,16 +1717,6 @@ public class EpdgTunnelManagerTest { PersistableBundle bundle = new PersistableBundle(); setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); - doReturn(null) .when(mMockIkeSessionCreator) .createIkeSession( @@ -1851,15 +1779,6 @@ public class EpdgTunnelManagerTest { bundle.putInt(CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, nattTimer); setupMockForGetConfig(bundle); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(false), - eq(false), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); doReturn(null) .when(mMockIkeSessionCreator) @@ -1914,7 +1833,6 @@ public class EpdgTunnelManagerTest { TunnelSetupRequest tsr = TunnelSetupRequest.builder() .setApnName(testApnName) - .setNetwork(mMockNetwork) .setApnIpProtocol(ApnSetting.PROTOCOL_IPV4V6) .setSrcIpv6Address(testAddressV6) .setSrcIpv6AddressPrefixLength(ipv6AddressLen) @@ -1926,15 +1844,6 @@ public class EpdgTunnelManagerTest { .build(); setupMockForGetConfig(null); - when(mMockEpdgSelector.getValidatedServerList( - anyInt(), - anyInt(), - anyInt(), - eq(isRoaming), - eq(isEmergency), - eq(mMockNetwork), - any(EpdgSelector.EpdgSelectorCallback.class))) - .thenReturn(new IwlanError(IwlanError.NO_ERROR)); doReturn(null) .when(mMockIkeSessionCreator) @@ -1961,7 +1870,7 @@ public class EpdgTunnelManagerTest { anyInt(), eq(isRoaming), eq(isEmergency), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any(EpdgSelector.EpdgSelectorCallback.class)); mEpdgTunnelManager.sendSelectionRequestComplete( @@ -1991,7 +1900,7 @@ public class EpdgTunnelManagerTest { assertEquals(ikeId.fqdn, testApnName); // verify Network - assertEquals(ikeSessionParams.getNetwork(), mMockNetwork); + assertEquals(ikeSessionParams.getNetwork(), mMockDefaultNetwork); // verify requestPcscf (true) with Apn protocol IPV6 // it should add the pcscf config requests of type ConfigRequestIpv6PcscfServer and @@ -2132,8 +2041,8 @@ public class EpdgTunnelManagerTest { int transactionId = 0; // testApnName with token 0 - setupTunnelBringup(TEST_APN_NAME, ++transactionId); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES, ++transactionId); + mEpdgTunnelManager.onConnectedToEpdg(true); IwlanError error = new IwlanError(mMockIkeException); doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(TEST_APN_NAME), eq(error)); @@ -2146,8 +2055,8 @@ public class EpdgTunnelManagerTest { assertNull(mEpdgTunnelManager.getTunnelConfigForApn(TEST_APN_NAME)); // testApnName1 with token 1 - setupTunnelBringup(TEST_APN_NAME, ++transactionId); - mEpdgTunnelManager.setHasConnectedToEpdg(true); + setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES, ++transactionId); + mEpdgTunnelManager.onConnectedToEpdg(true); // signal from obsolete callback (token 0), ignore it reset(mMockIwlanTunnelCallback); @@ -2191,7 +2100,7 @@ public class EpdgTunnelManagerTest { eq(EpdgSelector.IPV4_PREFERRED), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } @@ -2219,7 +2128,7 @@ public class EpdgTunnelManagerTest { eq(EpdgSelector.IPV6_PREFERRED), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } @@ -2247,7 +2156,7 @@ public class EpdgTunnelManagerTest { eq(EpdgSelector.SYSTEM_PREFERRED), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } @@ -2279,7 +2188,7 @@ public class EpdgTunnelManagerTest { eq(EpdgSelector.SYSTEM_PREFERRED), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } @@ -2310,7 +2219,7 @@ public class EpdgTunnelManagerTest { anyInt(), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(TEST_APN_NAME), eq(error)); verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error)); @@ -2340,12 +2249,12 @@ public class EpdgTunnelManagerTest { eq(EpdgSelector.SYSTEM_PREFERRED), eq(false), eq(false), - eq(mMockNetwork), + eq(mMockDefaultNetwork), any()); } @Test - public void testOnOpenedTunnelMetricsData() { + public void testOnOpenedTunnelMetricsData() throws Exception { doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME)); mEpdgTunnelManager.bringUpTunnel( getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP), @@ -2370,7 +2279,7 @@ public class EpdgTunnelManagerTest { IwlanError error = new IwlanError(IwlanError.IKE_INIT_TIMEOUT, mMockIkeIoException); doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error)); - setupTunnelBringup(testApnName, 1); + setupTunnelBringup(); ArgumentCaptor<EpdgTunnelManager.TmIkeSessionCallback> ikeSessionCallbackCaptor = ArgumentCaptor.forClass(EpdgTunnelManager.TmIkeSessionCallback.class); @@ -2392,42 +2301,34 @@ public class EpdgTunnelManagerTest { @Test public void testCloseTunnelWithIkeDpdTimeout() throws Exception { IwlanError error = new IwlanError(IwlanError.IKE_DPD_TIMEOUT, mMockIkeIoException); - doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(TEST_APN_NAME), eq(error)); IkeSessionArgumentCaptors ikeSessionArgumentCaptors = verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME); ChildSessionCallback childSessionCallback = ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback); - setOneTunnelOpened(TEST_APN_NAME); mEpdgTunnelManager .getTmIkeSessionCallback( TEST_APN_NAME, mEpdgTunnelManager.getCurrentTokenForApn(TEST_APN_NAME)) .onClosedWithException(mMockIkeIoException); mTestLooper.dispatchAll(); - verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(TEST_APN_NAME), eq(error)); - verify(mMockIwlanTunnelCallback, atLeastOnce()).onClosed(eq(TEST_APN_NAME), eq(error)); + verify(mEpdgTunnelManager, never()).reportIwlanError(eq(TEST_APN_NAME), eq(error)); + verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error)); } @Test public void testCloseTunnelWithIkeMobilityTimeout() throws Exception { IwlanError error = new IwlanError(IwlanError.IKE_MOBILITY_TIMEOUT, mMockIkeIoException); - doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(TEST_APN_NAME), eq(error)); IkeSessionArgumentCaptors ikeSessionArgumentCaptors = - verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME); + verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME, mMockIkeSession); ChildSessionCallback childSessionCallback = ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback); - setOneTunnelOpened(TEST_APN_NAME); - - mEpdgTunnelManager.updateNetwork(mMockNetwork, TEST_APN_NAME); - mTestLooper.dispatchAll(); - EpdgTunnelManager.TunnelConfig testApnTunnelConfig = - mEpdgTunnelManager.getTunnelConfigForApn(TEST_APN_NAME); - testApnTunnelConfig.getIkeSession().setNetwork(mMockNetwork); + Network newNetwork = mock(Network.class); + mEpdgTunnelManager.updateNetwork(newNetwork, mMockLinkProperties); mTestLooper.dispatchAll(); mEpdgTunnelManager @@ -2436,7 +2337,143 @@ public class EpdgTunnelManagerTest { .onClosedWithException(mMockIkeIoException); mTestLooper.dispatchAll(); - verify(mEpdgTunnelManager, atLeastOnce()).reportIwlanError(eq(TEST_APN_NAME), eq(error)); - verify(mMockIwlanTunnelCallback, atLeastOnce()).onClosed(eq(TEST_APN_NAME), eq(error)); + verify(mMockIkeSession, times(1)).setNetwork(eq(newNetwork)); + verify(mEpdgTunnelManager, never()).reportIwlanError(eq(TEST_APN_NAME), eq(error)); + verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error)); + } + + private boolean testIsN1ModeSupported(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( + testIsN1ModeSupported( + new int[] { + CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA, + CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA + })); + } + + @Test + public void testIsN1ModeSupportedFalse() throws Exception { + assertFalse( + testIsN1ModeSupported( + new int[] {CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA})); + } + + @Test + public void testUpdateNetworkToOpenedTunnel() throws Exception { + String apnName = "ims"; + + IkeSessionArgumentCaptors ikeSessionArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockIkeSession); + ChildSessionCallback childSessionCallback = + ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); + childSessionCallback.onIpSecTransformCreated( + mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN); + mTestLooper.dispatchAll(); + + mEpdgTunnelManager.onConnectedToEpdg(true); + Network newNetwork = mock(Network.class); + mEpdgTunnelManager.updateNetwork(newNetwork, mMockLinkProperties); + mTestLooper.dispatchAll(); + verify(mMockIkeSession, times(1)).setNetwork(eq(newNetwork)); + } + + @Test + public void testUpdateNetworkForIncomingSetupRequest() throws Exception { + String apnName = "ims"; + Network newNetwork = mock(Network.class); + + mEpdgTunnelManager.updateNetwork(newNetwork, mMockLinkProperties); + mTestLooper.dispatchAll(); + + IkeSessionArgumentCaptors ikeSessionArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockIkeSession); + ChildSessionCallback childSessionCallback = + ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); + childSessionCallback.onIpSecTransformCreated( + mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN); + mTestLooper.dispatchAll(); + + verify(mMockEpdgSelector, times(1)) + .getValidatedServerList( + anyInt(), /* transactionId */ + anyInt(), /* filter */ + anyInt(), /* order */ + eq(false), /* isRoaming */ + eq(false), /* isEmergency */ + eq(newNetwork), + any(EpdgSelector.EpdgSelectorCallback.class)); + IkeSessionParams ikeSessionParams = + ikeSessionArgumentCaptors.mIkeSessionParamsCaptor.getValue(); + assertEquals(ikeSessionParams.getNetwork(), newNetwork); + } + + @Test + public void testUpdateNullNetworkToOpenedTunnel() throws Exception { + String apnName = "ims"; + + IkeSessionArgumentCaptors ikeSessionArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockIkeSession); + ChildSessionCallback childSessionCallback = + ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); + childSessionCallback.onIpSecTransformCreated( + mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN); + mTestLooper.dispatchAll(); + + mEpdgTunnelManager.updateNetwork(null, null); + mTestLooper.dispatchAll(); + verify(mMockIkeSession, never()).setNetwork(any()); + } + + @Test + public void testUpdateNullNetworkAndRejectIncomingSetupRequest() throws Exception { + String apnName = "ims"; + + doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(apnName), any(IwlanError.class)); + + mEpdgTunnelManager.updateNetwork(null, null); + mTestLooper.dispatchAll(); + + mEpdgTunnelManager.bringUpTunnel( + getBasicTunnelSetupRequest(apnName, ApnSetting.PROTOCOL_IP), + mMockIwlanTunnelCallback, + mMockIwlanTunnelMetrics); + mTestLooper.dispatchAll(); + verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(apnName), any(IwlanError.class)); + } + + @Test + public void testUpdateUnreachableLinkProperties() throws Exception { + String apnName = "ims"; + + IkeSessionArgumentCaptors ikeSessionArgumentCaptors = + verifyBringUpTunnelWithDnsQuery(apnName, mMockIkeSession); + ChildSessionCallback childSessionCallback = + ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue(); + childSessionCallback.onIpSecTransformCreated( + mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN); + mTestLooper.dispatchAll(); + + mEpdgTunnelManager.onConnectedToEpdg(true); + Network newNetwork = mock(Network.class); + LinkProperties mockUnreachableLinkProperties = mock(LinkProperties.class); + when(mockUnreachableLinkProperties.isReachable(any())).thenReturn(false); + mEpdgTunnelManager.updateNetwork(newNetwork, mockUnreachableLinkProperties); + mTestLooper.dispatchAll(); + verify(mMockIkeSession, never()).setNetwork(eq(newNetwork)); + + mEpdgTunnelManager.updateNetwork(newNetwork, mMockLinkProperties); + mTestLooper.dispatchAll(); + verify(mMockIkeSession, times(1)).setNetwork(eq(newNetwork)); } } |