summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:50:42 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:50:42 +0000
commit9d7ee7aeb092ebebbbe7db8994f55656e0ebc024 (patch)
treecd1307fa58b8f58ed944e68eccffbd8e63dc6225
parentde24857d2f0220f7e1d042ccaa41aff36fa43bdf (diff)
parent26c3fa06288d0c97a3117f839a3360f3fce845f6 (diff)
downloadIwlan-android14-mainline-appsearch-release.tar.gz
Snap for 10453563 from 26c3fa06288d0c97a3117f839a3360f3fce845f6 to mainline-appsearch-releaseaml_ase_341510000aml_ase_341410000aml_ase_341310010aml_ase_341113000aml_ase_340913000android14-mainline-appsearch-release
Change-Id: I2316e34f5cd26115fe4c9d7914bffe545ba0b605
-rw-r--r--Android.bp10
-rw-r--r--AndroidManifest.xml9
-rw-r--r--assets/defaultiwlanerrorconfig.json19
-rw-r--r--com.google.android.iwlan.xml2
-rw-r--r--src/com/google/android/iwlan/ErrorPolicyManager.java438
-rw-r--r--src/com/google/android/iwlan/IwlanBroadcastReceiver.java9
-rw-r--r--src/com/google/android/iwlan/IwlanDataService.java1819
-rw-r--r--src/com/google/android/iwlan/IwlanError.java134
-rw-r--r--src/com/google/android/iwlan/IwlanEventListener.java121
-rw-r--r--src/com/google/android/iwlan/IwlanHelper.java48
-rw-r--r--src/com/google/android/iwlan/IwlanNetworkService.java441
-rw-r--r--src/com/google/android/iwlan/IwlanTunnelMetricsImpl.java45
-rw-r--r--src/com/google/android/iwlan/TunnelMetricsInterface.java141
-rw-r--r--src/com/google/android/iwlan/epdg/EpdgSelector.java681
-rw-r--r--src/com/google/android/iwlan/epdg/EpdgTunnelManager.java1232
-rw-r--r--src/com/google/android/iwlan/epdg/IkeSessionState.java55
-rw-r--r--src/com/google/android/iwlan/epdg/SrvDnsResolver.java3
-rw-r--r--src/com/google/android/iwlan/epdg/TunnelSetupRequest.java4
-rw-r--r--src/com/google/android/iwlan/proto/MetricsAtom.java182
-rw-r--r--test/com/google/android/iwlan/ErrorPolicyManagerTest.java624
-rw-r--r--test/com/google/android/iwlan/IwlanDataServiceTest.java1221
-rw-r--r--test/com/google/android/iwlan/IwlanEventListenerTest.java89
-rw-r--r--test/com/google/android/iwlan/IwlanNetworkServiceTest.java295
-rw-r--r--test/com/google/android/iwlan/TunnelMetricsInterfaceTest.java71
-rw-r--r--test/com/google/android/iwlan/epdg/EpdgSelectorTest.java620
-rw-r--r--test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java1142
26 files changed, 7053 insertions, 2402 deletions
diff --git a/Android.bp b/Android.bp
index 5994d3f..4d912dd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,12 +2,21 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+genrule {
+ name: "statslog-Iwlan-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module iwlan --javaPackage com.google.android.iwlan"
+ + " --javaClass IwlanStatsLog",
+ out: ["com/google/android/iwlan/IwlanStatsLog.java"],
+}
+
android_app {
name: "Iwlan",
manifest: "AndroidManifest.xml",
srcs: [
"src/**/*.java",
"src/**/I*.aidl",
+ ":statslog-Iwlan-java-gen",
],
resource_dirs: [
"res",
@@ -53,6 +62,7 @@ android_test {
srcs: [
"src/**/*.java",
"test/**/*.java",
+ ":statslog-Iwlan-java-gen",
],
platform_apis: true,
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6a7a2d4..e0b0b3a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,19 +8,14 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.MANAGE_IPSEC_TUNNELS" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_DELETE_CHILD" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_REKEY_CHILD" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_DELETE_IKE" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_REKEY_IKE" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_DPD" />
- <protected-broadcast android:name="IkeAlarmReceiver.ACTION_KEEPALIVE" />
-
<application
android:directBootAware="true"
android:defaultToDeviceProtectedStorage="true">
diff --git a/assets/defaultiwlanerrorconfig.json b/assets/defaultiwlanerrorconfig.json
index eae1ae2..2d2be95 100644
--- a/assets/defaultiwlanerrorconfig.json
+++ b/assets/defaultiwlanerrorconfig.json
@@ -50,6 +50,10 @@
# 3.4.3. APM_ENABLE_EVENT: APM off to on toggle.
# 3.4.4. WIFI_AP_CHANGED_EVENT: Wifi is connected to a AP with different SSID.
# 3.4.5. WIFI_CALLING_DISABLE_EVENT: Wifi calling button on to off toggle.
+# 3.5. "HandoverAttemptCount": Integer to specify the number of handover request attempts before
+# using initial attach instead. It is an optional field.
+# Note: This should only be defined in the config when handover attempt count is enabled and
+# "ErrorType" is explicitly defined as "IKE_PROTOCOL_ERROR_TYPE".
#
# Note: When the value is "*" for any of "ApnName" or "ErrorType" or "ErrorDetails",
# it means that the config definition applies to rest of the errors for which
@@ -64,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"],
- "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 467a2c9..f91bb8a 100644
--- a/com.google.android.iwlan.xml
+++ b/com.google.android.iwlan.xml
@@ -2,5 +2,7 @@
<permissions>
<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 59fad64..88e9a7f 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -32,12 +32,13 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.google.auto.value.AutoValue;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -50,6 +51,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -70,8 +72,8 @@ public class ErrorPolicyManager {
/**
* This value represents rest of the errors that are not defined above. ErrorDetails should
- * mention the specific error. If it doesn't not - the policy will be used as a fallback global
- * policy. Currently Supported ErrorDetails "IO_EXCEPTION" "TIMEOUT_EXCEPTION"
+ * mention the specific error. If it doesn't - the policy will be used as a fallback global
+ * policy. Currently, Supported ErrorDetails "IO_EXCEPTION" "TIMEOUT_EXCEPTION"
* "SERVER_SELECTION_FAILED" "TUNNEL_TRANSFORM_FAILED"
*/
private static final int GENERIC_ERROR_TYPE = 2;
@@ -86,8 +88,13 @@ public class ErrorPolicyManager {
*/
private static final int IKE_PROTOCOL_ERROR_TYPE = 3;
+ static ErrorPolicy.Builder builder() {
+ return new AutoValue_ErrorPolicyManager_ErrorPolicy.Builder()
+ .setInfiniteRetriesWithLastRetryTime(false);
+ }
+
@IntDef({UNKNOWN_ERROR_TYPE, FALLBACK_ERROR_TYPE, GENERIC_ERROR_TYPE, IKE_PROTOCOL_ERROR_TYPE})
- @interface ErrorPolicyErrorType {};
+ @interface ErrorPolicyErrorType {}
private static final String[] GENERIC_ERROR_DETAIL_STRINGS = {
"*",
@@ -116,31 +123,14 @@ public class ErrorPolicyManager {
private static final int IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED = 11011;
private static final int IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055;
- @IntDef({
- IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION,
- IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED,
- IKE_PROTOCOL_ERROR_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
- IKE_PROTOCOL_ERROR_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION,
- IKE_PROTOCOL_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTERS,
- IKE_PROTOCOL_ERROR_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS,
- IKE_PROTOCOL_ERROR_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED,
- IKE_PROTOCOL_ERROR_USER_UNKNOWN,
- IKE_PROTOCOL_ERROR_NO_APN_SUBSCRIPTION,
- IKE_PROTOCOL_ERROR_AUTHORIZATION_REJECTED,
- IKE_PROTOCOL_ERROR_ILLEGAL_ME,
- IKE_PROTOCOL_ERROR_NETWORK_FAILURE,
- IKE_PROTOCOL_ERROR_RAT_TYPE_NOT_ALLOWED,
- IKE_PROTOCOL_ERROR_IMEI_NOT_ACCEPTED,
- IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED,
- IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED
- })
- @interface IkeProtocolErrorType {};
+ /** Private IKEv2 notify message types, as defined in TS 124 502 (section 9.2.4.1) */
+ private static final int IKE_PROTOCOL_ERROR_CONGESTION = 15500;
private final String LOG_TAG;
- private static Map<Integer, ErrorPolicyManager> mInstances = new ConcurrentHashMap<>();
- private Context mContext;
- private int mSlotId;
+ private static final Map<Integer, ErrorPolicyManager> mInstances = new ConcurrentHashMap<>();
+ private final Context mContext;
+ private final int mSlotId;
// Policies read from defaultiwlanerrorconfig.json
// String APN as key to identify the ErrorPolicies associated with it.
@@ -148,15 +138,19 @@ public class ErrorPolicyManager {
// Policies read from CarrierConfig
// String APN as key to identify the ErrorPolicies associated with it.
- private Map<String, List<ErrorPolicy>> mCarrierConfigPolicies = new HashMap<>();
+ private final Map<String, List<ErrorPolicy>> mCarrierConfigPolicies = new HashMap<>();
// String APN as key to identify the ErrorInfo associated with that APN
- private Map<String, ErrorInfo> mLastErrorForApn = new ConcurrentHashMap<>();
+ private final Map<String, ErrorInfo> mLastErrorForApn = new ConcurrentHashMap<>();
+
+ // Records the most recently reported IwlanError (including NO_ERROR), and the corresponding
+ // APN.
+ private ApnWithIwlanError mMostRecentError;
// List of current Unthrottling events registered with IwlanEventListener
private Set<Integer> mUnthrottlingEvents;
- private ErrorStats mErrorStats = new ErrorStats();
+ private final ErrorStats mErrorStats = new ErrorStats();
private HandlerThread mHandlerThread;
@VisibleForTesting Handler mHandler;
@@ -178,11 +172,13 @@ public class ErrorPolicyManager {
return mInstances.computeIfAbsent(slotId, k -> new ErrorPolicyManager(context, slotId));
}
+ @VisibleForTesting
+ public static void resetAllInstances() {
+ mInstances.clear();
+ }
+
/**
* Release or reset the instance.
- *
- * @param context
- * @param slotId
*/
public void releaseInstance() {
Log.d(LOG_TAG, "Release Instance with slotId: " + mSlotId);
@@ -202,6 +198,7 @@ public class ErrorPolicyManager {
public synchronized long reportIwlanError(String apn, IwlanError iwlanError) {
// Fail by default
long retryTime = -1;
+ mMostRecentError = new ApnWithIwlanError(apn, iwlanError);
if (iwlanError.getErrorType() == IwlanError.NO_ERROR) {
Log.d(LOG_TAG, "reportIwlanError: NO_ERROR");
@@ -218,7 +215,7 @@ public class ErrorPolicyManager {
}
if (!mLastErrorForApn.containsKey(apn)
|| !mLastErrorForApn.get(apn).getError().equals(iwlanError)) {
- Log.d(LOG_TAG, "Doesn't match to the previous error" + iwlanError.toString());
+ Log.d(LOG_TAG, "Doesn't match to the previous error" + iwlanError);
ErrorPolicy policy = findErrorPolicy(apn, iwlanError);
ErrorInfo errorInfo = new ErrorInfo(iwlanError, policy);
mLastErrorForApn.put(apn, errorInfo);
@@ -233,7 +230,7 @@ public class ErrorPolicyManager {
*
* @param apn apn name for which the error happened
* @param iwlanError Error
- * @param long backOffTime in seconds
+ * @param backOffTime in seconds
* @return retry time which is the backoff time. -1 if it is NO_ERROR
*/
public synchronized long reportIwlanError(String apn, IwlanError iwlanError, long backOffTime) {
@@ -256,7 +253,7 @@ public class ErrorPolicyManager {
retryTime = backOffTime;
if (!mLastErrorForApn.containsKey(apn)
|| !mLastErrorForApn.get(apn).getError().equals(iwlanError)) {
- Log.d(LOG_TAG, "Doesn't match to the previous error" + iwlanError.toString());
+ Log.d(LOG_TAG, "Doesn't match to the previous error" + iwlanError);
ErrorPolicy policy = findErrorPolicy(apn, iwlanError);
ErrorInfo errorInfo = new ErrorInfo(iwlanError, policy, backOffTime);
mLastErrorForApn.put(apn, errorInfo);
@@ -292,28 +289,54 @@ public class ErrorPolicyManager {
* @return DataFailCause corresponding to the error for the apn
*/
public synchronized int getDataFailCause(String apn) {
-
if (!mLastErrorForApn.containsKey(apn)) {
return DataFailCause.NONE;
}
IwlanError error = mLastErrorForApn.get(apn).getError();
+ return getDataFailCause(error);
+ }
+
+ private int getDataFailCause(IwlanError error) {
int ret = DataFailCause.ERROR_UNSPECIFIED;
- if (error.getErrorType() == IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED) {
+
+ if (error.getErrorType() == IwlanError.NO_ERROR) {
+ ret = DataFailCause.NONE;
+ } else if (error.getErrorType() == IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED) {
ret = DataFailCause.IWLAN_DNS_RESOLUTION_NAME_FAILURE;
+ } else if (error.getErrorType() == IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED) {
+ ret = DataFailCause.ONLY_IPV4_ALLOWED;
+ } else if (error.getErrorType() == IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED) {
+ ret = DataFailCause.ONLY_IPV6_ALLOWED;
} else if (error.getErrorType() == IwlanError.IKE_INTERNAL_IO_EXCEPTION) {
ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT;
} else if (error.getErrorType() == IwlanError.SIM_NOT_READY_EXCEPTION) {
- ret = DataFailCause.IWLAN_PDN_CONNECTION_REJECTION;
- } else if (error.getErrorType() == IwlanError.NETWORK_FAILURE) {
- ret = DataFailCause.NETWORK_FAILURE;
+ ret = DataFailCause.SIM_CARD_CHANGED;
+ } else if (error.getErrorType()
+ == IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED) {
+ 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_IKE_INIT_TIMEOUT;
+ } else if (error.getErrorType() == IwlanError.IKE_MOBILITY_TIMEOUT) {
+ ret = DataFailCause.IWLAN_IKE_MOBILITY_TIMEOUT;
+ } else if (error.getErrorType() == IwlanError.IKE_DPD_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 != null && exception instanceof IkeProtocolException) {
+ if (exception instanceof IkeProtocolException) {
int protocolErrorType = ((IkeProtocolException) exception).getErrorType();
switch (protocolErrorType) {
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;
@@ -362,8 +385,11 @@ public class ErrorPolicyManager {
case IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED:
ret = DataFailCause.IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED;
break;
+ case IKE_PROTOCOL_ERROR_CONGESTION:
+ ret = DataFailCause.IWLAN_CONGESTION;
+ break;
default:
- ret = DataFailCause.IWLAN_NETWORK_FAILURE;
+ ret = DataFailCause.IWLAN_IKE_PRIVATE_PROTOCOL_ERROR;
break;
}
}
@@ -371,6 +397,13 @@ public class ErrorPolicyManager {
return ret;
}
+ public synchronized int getMostRecentDataFailCause() {
+ if (mMostRecentError != null) {
+ return getDataFailCause(mMostRecentError.mIwlanError);
+ }
+ return DataFailCause.NONE;
+ }
+
/**
* Returns the current retryTime based on the lastErrorForApn
*
@@ -385,6 +418,23 @@ public class ErrorPolicyManager {
}
/**
+ * Returns the index of the FQDN to use for ePDG server selection, based on how many FQDNs are
+ * available, the position of the RetryArray index, and configuration of 'NumAttemptsPerFqdn'.
+ *
+ * @param numFqdns number of FQDNs discovered during ePDG server selection.
+ * @return int index of the FQDN to use for ePDG server selection. -1 (invalid) if RetryArray or
+ * 'NumAttemptsPerFqdn' is not specified in the ErrorPolicy.
+ */
+ public synchronized int getCurrentFqdnIndex(int numFqdns) {
+ String apn = mMostRecentError.mApn;
+ if (!mLastErrorForApn.containsKey(apn)) {
+ return -1;
+ }
+ ErrorInfo errorInfo = mLastErrorForApn.get(apn);
+ return errorInfo.getCurrentFqdnIndex(numFqdns);
+ }
+
+ /**
* Returns the last error for that apn
*
* @param apn apn name
@@ -397,6 +447,19 @@ public class ErrorPolicyManager {
return new IwlanError(IwlanError.NO_ERROR);
}
+ /**
+ * Returns whether framework should retry tunnel setup with initial PDN bringup request when
+ * handover request fails.
+ *
+ * @param apn apn name
+ * @return boolean result of whether framework should retry tunnel setup with initial PDN
+ * bringup request when handover request fails
+ */
+ public synchronized boolean shouldRetryWithInitialAttach(String apn) {
+ ErrorInfo errorInfo = mLastErrorForApn.get(apn);
+ return errorInfo != null && errorInfo.shouldRetryWithInitialAttach();
+ }
+
public void logErrorPolicies() {
Log.d(LOG_TAG, "mCarrierConfigPolicies:");
for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
@@ -414,7 +477,7 @@ public class ErrorPolicyManager {
}
}
- public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public synchronized void dump(PrintWriter pw) {
pw.println("---- ErrorPolicyManager ----");
for (Map.Entry<String, ErrorInfo> entry : mLastErrorForApn.entrySet()) {
pw.print("APN: " + entry.getKey() + " IwlanError: " + entry.getValue().getError());
@@ -484,14 +547,20 @@ public class ErrorPolicyManager {
return selectedPolicy;
}
- private void initHandler() {
+ @VisibleForTesting
+ void initHandler() {
+ mHandler = new EpmHandler(getLooper());
+ }
+
+ @VisibleForTesting
+ Looper getLooper() {
mHandlerThread = new HandlerThread("ErrorPolicyManagerThread");
mHandlerThread.start();
- mHandler = new EpmHandler(mHandlerThread.getLooper());
+ return mHandlerThread.getLooper();
}
private String getDefaultJSONConfig() throws IOException {
- String str = "";
+ String str;
StringBuilder stringBuilder = new StringBuilder();
InputStream is = mContext.getAssets().open("defaultiwlanerrorconfig.json");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
@@ -507,7 +576,8 @@ public class ErrorPolicyManager {
return stringBuilder.toString();
}
- private Map<String, List<ErrorPolicy>> readErrorPolicies(JSONArray apnArray)
+ @VisibleForTesting
+ Map<String, List<ErrorPolicy>> readErrorPolicies(JSONArray apnArray)
throws JSONException, IllegalArgumentException {
Map<String, List<ErrorPolicy>> errorPolicies = new HashMap<>();
for (int i = 0; i < apnArray.length(); i++) {
@@ -521,21 +591,47 @@ public class ErrorPolicyManager {
String errorTypeStr = ((String) errorTypeObject.get("ErrorType")).trim();
JSONArray errorDetailArray = (JSONArray) errorTypeObject.get("ErrorDetails");
- int errorType = UNKNOWN_ERROR_TYPE;
+ int errorType;
if ((errorType = getErrorPolicyErrorType(errorTypeStr)) == UNKNOWN_ERROR_TYPE) {
throw new IllegalArgumentException("Unknown error type in the parsing");
}
- ErrorPolicy errorPolicy =
- new ErrorPolicy(
- errorType,
- parseErrorDetails(errorType, errorDetailArray),
- parseRetryArray((JSONArray) errorTypeObject.get("RetryArray")),
- parseUnthrottlingEvents(
- (JSONArray) errorTypeObject.get("UnthrottlingEvents")));
+ List<Integer> retryArray =
+ parseRetryArray((JSONArray) errorTypeObject.get("RetryArray"));
+
+ ErrorPolicy.Builder errorPolicyBuilder =
+ builder()
+ .setErrorType(errorType)
+ .setErrorDetails(parseErrorDetails(errorType, errorDetailArray))
+ .setRetryArray(retryArray)
+ .setUnthrottlingEvents(
+ parseUnthrottlingEvents(
+ (JSONArray)
+ errorTypeObject.get("UnthrottlingEvents")));
+
+ if (!retryArray.isEmpty() && retryArray.get(retryArray.size() - 1) == -1L) {
+ errorPolicyBuilder.setInfiniteRetriesWithLastRetryTime(true);
+ }
+
+ if (errorTypeObject.has("NumAttemptsPerFqdn")) {
+ errorPolicyBuilder.setNumAttemptsPerFqdn(
+ errorTypeObject.getInt("NumAttemptsPerFqdn"));
+ }
+
+ if (errorTypeObject.has("HandoverAttemptCount")) {
+ if (errorType != IKE_PROTOCOL_ERROR_TYPE) {
+ throw new IllegalArgumentException(
+ "Handover attempt count should not be applied when errorType is not"
+ + " explicitly defined as IKE_PROTOCOL_ERROR_TYPE");
+ }
+ errorPolicyBuilder.setHandoverAttemptCount(
+ errorTypeObject.getInt("HandoverAttemptCount"));
+ }
+
+ ErrorPolicy errorPolicy = errorPolicyBuilder.build();
- errorPolicies.putIfAbsent(apnName, new ArrayList<ErrorPolicy>());
+ errorPolicies.putIfAbsent(apnName, new ArrayList<>());
errorPolicies.get(apnName).add(errorPolicy);
}
}
@@ -551,7 +647,7 @@ public class ErrorPolicyManager {
// catch misplaced -1 retry times in the array.
// 1. if it is not placed at the last position in the array
// 2. if it is placed in the first position (catches the case where it is
- // the only element.
+ // the only element).
if (retryTime.equals("-1") && (i != retryArray.length() - 1 || i == 0)) {
throw new IllegalArgumentException("Misplaced -1 in retry array");
}
@@ -621,7 +717,7 @@ public class ErrorPolicyManager {
boolean ret = true;
if (errorDetailStr.contains("-")) {
// verify range format
- String rangeNumbers[] = errorDetailStr.split("-");
+ String[] rangeNumbers = errorDetailStr.split("-");
if (rangeNumbers.length == 2) {
for (String range : rangeNumbers) {
if (!TextUtils.isDigitsOnly(range)) {
@@ -673,13 +769,13 @@ public class ErrorPolicyManager {
for (Map.Entry<String, List<ErrorPolicy>> entry : mCarrierConfigPolicies.entrySet()) {
List<ErrorPolicy> errorPolicies = entry.getValue();
for (ErrorPolicy errorPolicy : errorPolicies) {
- events.addAll(errorPolicy.mUnthrottlingEvents);
+ events.addAll(errorPolicy.unthrottlingEvents());
}
}
for (Map.Entry<String, List<ErrorPolicy>> entry : mDefaultPolicies.entrySet()) {
List<ErrorPolicy> errorPolicies = entry.getValue();
for (ErrorPolicy errorPolicy : errorPolicies) {
- events.addAll(errorPolicy.mUnthrottlingEvents);
+ events.addAll(errorPolicy.unthrottlingEvents());
}
}
events.add(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT);
@@ -693,7 +789,7 @@ public class ErrorPolicyManager {
*/
private synchronized void readFromCarrierConfig(int currentCarrierId) {
String carrierConfigErrorPolicy =
- (String) IwlanHelper.getConfig(KEY_ERROR_POLICY_CONFIG_STRING, mContext, mSlotId);
+ IwlanHelper.getConfig(KEY_ERROR_POLICY_CONFIG_STRING, mContext, mSlotId);
if (carrierConfigErrorPolicy == null) {
Log.e(LOG_TAG, "ErrorPolicy from Carrier Config is NULL");
return;
@@ -712,9 +808,7 @@ public class ErrorPolicyManager {
LOG_TAG,
"Unable to parse the ErrorPolicy from CarrierConfig\n"
+ carrierConfigErrorPolicy);
- if (mCarrierConfigPolicies != null) {
- mCarrierConfigPolicies.clear();
- }
+ mCarrierConfigPolicies.clear();
carrierConfigErrorPolicyString = null;
e.printStackTrace();
}
@@ -726,18 +820,16 @@ public class ErrorPolicyManager {
registerEvents = getAllUnthrottlingEvents();
mUnthrottlingEvents = getAllUnthrottlingEvents();
- if (registerEvents != null && unregisterEvents != null) {
+ if (unregisterEvents != null) {
registerEvents.removeAll(unregisterEvents);
unregisterEvents.removeAll(mUnthrottlingEvents);
}
- if (registerEvents != null) {
- IwlanEventListener.getInstance(mContext, mSlotId)
- .addEventListener(new ArrayList<Integer>(registerEvents), mHandler);
- }
+ IwlanEventListener.getInstance(mContext, mSlotId)
+ .addEventListener(new ArrayList<>(registerEvents), mHandler);
if (unregisterEvents != null) {
IwlanEventListener.getInstance(mContext, mSlotId)
- .removeEventListener(new ArrayList<Integer>(unregisterEvents), mHandler);
+ .removeEventListener(new ArrayList<>(unregisterEvents), mHandler);
}
Log.d(
LOG_TAG,
@@ -774,35 +866,55 @@ public class ErrorPolicyManager {
return mErrorStats;
}
- class ErrorPolicy {
- @ErrorPolicyErrorType int mErrorType;
- List<String> mErrorDetails;
- List<Integer> mRetryArray;
- List<Integer> mUnthrottlingEvents;
+ @AutoValue
+ abstract static class ErrorPolicy {
+ private static final String LOG_TAG = ErrorPolicyManager.class.getSimpleName();
+
+ abstract @ErrorPolicyErrorType int errorType();
+
+ abstract List<String> errorDetails();
+
+ abstract List<Integer> retryArray();
+
+ abstract Boolean infiniteRetriesWithLastRetryTime();
+
+ abstract List<Integer> unthrottlingEvents();
+
+ abstract Optional<Integer> numAttemptsPerFqdn();
+
+ abstract Optional<Integer> handoverAttemptCount();
- ErrorPolicy(
- @ErrorPolicyErrorType int errorType,
- List<String> errorDetails,
- List<Integer> retryArray,
- List<Integer> unthrottlingEvents) {
- mErrorType = errorType;
- mErrorDetails = errorDetails;
- mRetryArray = retryArray;
- mUnthrottlingEvents = unthrottlingEvents;
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setErrorType(int errorType);
+
+ abstract Builder setErrorDetails(List<String> errorDetails);
+
+ abstract Builder setRetryArray(List<Integer> retryArray);
+
+ abstract Builder setInfiniteRetriesWithLastRetryTime(
+ Boolean infiniteRetriesWithLastRetryTime);
+
+ abstract Builder setUnthrottlingEvents(List<Integer> unthrottlingEvents);
+
+ abstract Builder setNumAttemptsPerFqdn(Integer numAttemptsPerFqdn);
+
+ abstract Builder setHandoverAttemptCount(Integer handoverAttemptCount);
+
+ abstract ErrorPolicy build();
}
long getRetryTime(int index) {
long retryTime = -1;
- if (mRetryArray.size() > 0) {
+ if (retryArray().size() > 0) {
// If the index is greater than or equal to the last element's index
// and if the last item in the retryArray is "-1" use the retryTime
// of the element before the last element to repeat the element.
- if (index >= mRetryArray.size() - 1
- && mRetryArray.get(mRetryArray.size() - 1) == -1L) {
- index = mRetryArray.size() - 2;
+ if (infiniteRetriesWithLastRetryTime()) {
+ index = Math.min(index, retryArray().size() - 2);
}
- if (index >= 0 && index < mRetryArray.size()) {
- retryTime = mRetryArray.get(index);
+ if (index >= 0 && index < retryArray().size()) {
+ retryTime = retryArray().get(index);
}
}
@@ -814,25 +926,39 @@ public class ErrorPolicyManager {
return retryTime;
}
+ int getCurrentFqdnIndex(int retryIndex, int numFqdns) {
+ int result = -1;
+ if (numAttemptsPerFqdn().isEmpty() || retryArray().size() <= 0) {
+ return result;
+ }
+ // Cycles between 0 and (numFqdns - 1), based on the current attempt count and size of
+ // mRetryArray.
+ return (retryIndex + 1) / numAttemptsPerFqdn().get() % numFqdns;
+ }
+
@ErrorPolicyErrorType
int getErrorType() {
- return mErrorType;
+ return errorType();
+ }
+
+ int getHandoverAttemptCount() {
+ return handoverAttemptCount().orElse(Integer.MAX_VALUE);
}
synchronized boolean canUnthrottle(int event) {
- return mUnthrottlingEvents.contains(event);
+ return unthrottlingEvents().contains(event);
}
boolean match(IwlanError iwlanError) {
// Generic by default to match to generic policy.
- String iwlanErrorDetail = "*";
- if (mErrorType == FALLBACK_ERROR_TYPE) {
+ String iwlanErrorDetail;
+ if (errorType() == FALLBACK_ERROR_TYPE) {
return true;
- } else if (mErrorType == IKE_PROTOCOL_ERROR_TYPE
+ } else if (errorType() == IKE_PROTOCOL_ERROR_TYPE
&& iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) {
IkeProtocolException exception = (IkeProtocolException) iwlanError.getException();
iwlanErrorDetail = String.valueOf(exception.getErrorType());
- } else if (mErrorType == GENERIC_ERROR_TYPE) {
+ } else if (errorType() == GENERIC_ERROR_TYPE) {
iwlanErrorDetail = getGenericErrorDetailString(iwlanError);
if (iwlanErrorDetail.equals("UNKNOWN")) {
return false;
@@ -842,14 +968,14 @@ public class ErrorPolicyManager {
}
boolean ret = false;
- for (String errorDetail : mErrorDetails) {
- if (mErrorType == IKE_PROTOCOL_ERROR_TYPE
+ for (String errorDetail : errorDetails()) {
+ if (errorType() == IKE_PROTOCOL_ERROR_TYPE
&& iwlanError.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION
&& errorDetail.contains("-")) {
// error detail is stored in range format.
// ErrorPolicyManager#verifyIkeProtocolErrorDetail will make sure that
// this is stored correctly in "min-max" format.
- String range[] = errorDetail.split("-");
+ String[] range = errorDetail.split("-");
int min = Integer.parseInt(range[0]);
int max = Integer.parseInt(range[1]);
int error = Integer.parseInt(iwlanErrorDetail);
@@ -866,18 +992,22 @@ public class ErrorPolicyManager {
}
void log() {
- Log.d(LOG_TAG, "ErrorType: " + mErrorType);
- Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(mErrorDetails.toArray()));
- Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(mRetryArray.toArray()));
- Log.d(LOG_TAG, "unthrottlingEvents: " + Arrays.toString(mUnthrottlingEvents.toArray()));
+ Log.d(LOG_TAG, "ErrorType: " + errorType());
+ Log.d(LOG_TAG, "ErrorDetail: " + Arrays.toString(errorDetails().toArray()));
+ Log.d(LOG_TAG, "RetryArray: " + Arrays.toString(retryArray().toArray()));
+ Log.d(
+ LOG_TAG,
+ "InfiniteRetriesWithLastRetryTime: " + infiniteRetriesWithLastRetryTime());
+ Log.d(
+ LOG_TAG,
+ "UnthrottlingEvents: " + Arrays.toString(unthrottlingEvents().toArray()));
+ Log.d(LOG_TAG, "NumAttemptsPerFqdn: " + numAttemptsPerFqdn());
+ Log.d(LOG_TAG, "handoverAttemptCount: " + handoverAttemptCount());
}
boolean isFallback() {
- if ((mErrorType == FALLBACK_ERROR_TYPE)
- || (mErrorDetails.size() == 1 && mErrorDetails.get(0).equals("*"))) {
- return true;
- }
- return false;
+ return (errorType() == FALLBACK_ERROR_TYPE)
+ || (errorDetails().size() == 1 && errorDetails().get(0).equals("*"));
}
String getGenericErrorDetailString(IwlanError iwlanError) {
@@ -892,7 +1022,25 @@ public class ErrorPolicyManager {
case IwlanError.TUNNEL_TRANSFORM_FAILED:
ret = "TUNNEL_TRANSFORM_FAILED";
break;
+ case IwlanError.IKE_NETWORK_LOST_EXCEPTION:
+ ret = "IKE_NETWORK_LOST_EXCEPTION";
+ break;
+ case IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED:
+ ret = "EPDG_ADDRESS_ONLY_IPV4_ALLOWED";
+ break;
+ case IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED:
+ ret = "EPDG_ADDRESS_ONLY_IPV6_ALLOWED";
+ break;
// TODO: Add TIMEOUT_EXCEPTION processing
+ case IwlanError.IKE_INIT_TIMEOUT:
+ ret = "IKE_INIT_TIMEOUT";
+ break;
+ case IwlanError.IKE_MOBILITY_TIMEOUT:
+ ret = "IKE_MOBILITY_TIMEOUT";
+ break;
+ case IwlanError.IKE_DPD_TIMEOUT:
+ ret = "IKE_DPD_TIMEOUT";
+ break;
}
return ret;
}
@@ -901,16 +1049,19 @@ public class ErrorPolicyManager {
class ErrorInfo {
IwlanError mError;
ErrorPolicy mErrorPolicy;
+
+ // For the lifetime of the ErrorInfo object, this is a monotonically incremented value that
+ // can go beyond the size of mErrorPolicy's mRetryArray.
int mCurrentRetryIndex;
long mLastErrorTime;
- boolean mIsBackOffTimeValid = false;
+ boolean mIsBackOffTimeValid;
long mBackOffTime;
ErrorInfo(IwlanError error, ErrorPolicy errorPolicy) {
mError = error;
mErrorPolicy = errorPolicy;
mCurrentRetryIndex = -1;
- mLastErrorTime = new Date().getTime();
+ mLastErrorTime = IwlanHelper.elapsedRealtime();
}
ErrorInfo(IwlanError error, ErrorPolicy errorPolicy, long backOffTime) {
@@ -919,7 +1070,7 @@ public class ErrorPolicyManager {
mCurrentRetryIndex = -1;
mIsBackOffTimeValid = true;
mBackOffTime = backOffTime;
- mLastErrorTime = new Date().getTime();
+ mLastErrorTime = IwlanHelper.elapsedRealtime();
}
/**
@@ -931,7 +1082,7 @@ public class ErrorPolicyManager {
return -1;
}
long time = mErrorPolicy.getRetryTime(++mCurrentRetryIndex);
- mLastErrorTime = new Date().getTime();
+ mLastErrorTime = IwlanHelper.elapsedRealtime();
Log.d(LOG_TAG, "Current RetryArray index: " + mCurrentRetryIndex + " time: " + time);
return time;
}
@@ -950,7 +1101,7 @@ public class ErrorPolicyManager {
} else {
time = TimeUnit.SECONDS.toMillis(mErrorPolicy.getRetryTime(mCurrentRetryIndex));
}
- long currentTime = new Date().getTime();
+ long currentTime = IwlanHelper.elapsedRealtime();
time = Math.max(0, time - (currentTime - mLastErrorTime));
Log.d(
LOG_TAG,
@@ -958,13 +1109,18 @@ public class ErrorPolicyManager {
return time;
}
+ int getCurrentFqdnIndex(int numFqdns) {
+ ErrorPolicy errorPolicy = getErrorPolicy();
+ return errorPolicy.getCurrentFqdnIndex(mCurrentRetryIndex, numFqdns);
+ }
+
boolean isBackOffTimeValid() {
return mIsBackOffTimeValid;
}
void setBackOffTime(long backOffTime) {
mBackOffTime = backOffTime;
- mLastErrorTime = new Date().getTime();
+ mLastErrorTime = IwlanHelper.elapsedRealtime();
}
boolean canBringUpTunnel() {
@@ -979,7 +1135,7 @@ public class ErrorPolicyManager {
retryTime =
TimeUnit.SECONDS.toMillis(mErrorPolicy.getRetryTime(mCurrentRetryIndex));
}
- long currentTime = new Date().getTime();
+ long currentTime = IwlanHelper.elapsedRealtime();
long timeDifference = currentTime - mLastErrorTime;
if (timeDifference < retryTime) {
ret = false;
@@ -987,6 +1143,15 @@ public class ErrorPolicyManager {
return ret;
}
+ boolean shouldRetryWithInitialAttach() {
+ // UE should only uses initial attach to reset network failure, not for UE internal or
+ // DNS errors. When the number of handover failures due to network issues exceeds the
+ // configured threshold, UE should request network with initial attach instead of
+ // handover request.
+ return mErrorPolicy.getErrorType() == IKE_PROTOCOL_ERROR_TYPE
+ && mCurrentRetryIndex + 1 >= mErrorPolicy.getHandoverAttemptCount();
+ }
+
ErrorPolicy getErrorPolicy() {
return mErrorPolicy;
}
@@ -996,15 +1161,23 @@ public class ErrorPolicyManager {
}
}
+ static class ApnWithIwlanError {
+ @NonNull final String mApn;
+ @NonNull final IwlanError mIwlanError;
+
+ ApnWithIwlanError(@NonNull String apn, @NonNull IwlanError iwlanError) {
+ mApn = apn;
+ mIwlanError = iwlanError;
+ }
+ }
+
private boolean isValidCarrierConfigChangedEvent(int currentCarrierId) {
String errorPolicyConfig =
- (String) IwlanHelper.getConfig(KEY_ERROR_POLICY_CONFIG_STRING, mContext, mSlotId);
- boolean isValidEvent =
- (currentCarrierId != carrierId)
- || (carrierConfigErrorPolicyString == null)
- || (errorPolicyConfig != null
- && !carrierConfigErrorPolicyString.equals(errorPolicyConfig));
- return isValidEvent;
+ IwlanHelper.getConfig(KEY_ERROR_POLICY_CONFIG_STRING, mContext, mSlotId);
+ return (currentCarrierId != carrierId)
+ || (carrierConfigErrorPolicyString == null)
+ || (errorPolicyConfig != null
+ && !carrierConfigErrorPolicyString.equals(errorPolicyConfig));
}
private final class EpmHandler extends Handler {
@@ -1042,12 +1215,12 @@ public class ErrorPolicyManager {
}
@VisibleForTesting
- class ErrorStats {
+ static class ErrorStats {
@VisibleForTesting Map<String, Map<String, Long>> mStats = new HashMap<>();
private Date mStartTime;
- private int mStatCount = 0;
- private final int APN_COUNT_MAX = 10;
- private final int ERROR_COUNT_MAX = 1000;
+ private int mStatCount;
+ private static final int APN_COUNT_MAX = 10;
+ private static final int ERROR_COUNT_MAX = 1000;
ErrorStats() {
mStartTime = Calendar.getInstance().getTime();
@@ -1059,7 +1232,7 @@ public class ErrorPolicyManager {
reset();
}
if (!mStats.containsKey(apn)) {
- mStats.put(apn, new HashMap<String, Long>());
+ mStats.put(apn, new HashMap<>());
}
Map<String, Long> errorMap = mStats.get(apn);
String errorString = error.toString();
@@ -1074,19 +1247,22 @@ public class ErrorPolicyManager {
void reset() {
mStartTime = Calendar.getInstance().getTime();
- mStats = new HashMap<String, Map<String, Long>>();
+ mStats = new HashMap<>();
mStatCount = 0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("mStartTime: " + mStartTime);
+ sb.append("mStartTime: ").append(mStartTime);
sb.append("\nErrorStats");
for (Map.Entry<String, Map<String, Long>> entry : mStats.entrySet()) {
- sb.append("\n\tApn: " + entry.getKey());
+ sb.append("\n\tApn: ").append(entry.getKey());
for (Map.Entry<String, Long> errorEntry : entry.getValue().entrySet()) {
- sb.append("\n\t " + errorEntry.getKey() + " : " + errorEntry.getValue());
+ sb.append("\n\t ")
+ .append(errorEntry.getKey())
+ .append(" : ")
+ .append(errorEntry.getValue());
}
}
return sb.toString();
diff --git a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
index 10b10e4..e6195ba 100644
--- a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
+++ b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
@@ -29,6 +29,8 @@ import android.util.Log;
import com.google.android.iwlan.epdg.EpdgSelector;
+import java.util.Arrays;
+
public class IwlanBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "IwlanBroadcastReceiver";
@@ -96,7 +98,12 @@ public class IwlanBroadcastReceiver extends BroadcastReceiver {
int pcoId = intent.getIntExtra(TelephonyManager.EXTRA_PCO_ID, 0);
byte[] pcoData = intent.getByteArrayExtra(TelephonyManager.EXTRA_PCO_VALUE);
- Log.d(TAG, "PcoID:" + String.format("0x%04x", pcoId) + " PcoData:" + pcoData);
+ Log.d(
+ TAG,
+ "PcoID:"
+ + String.format("0x%04x", pcoId)
+ + " PcoData:"
+ + Arrays.toString(pcoData));
Context mContext = IwlanDataService.getContext();
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index a254535..04b6789 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -26,18 +26,27 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.support.annotation.GuardedBy;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoNr;
+import android.telephony.CellInfoWcdma;
import android.telephony.DataFailCause;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -51,10 +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 java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,45 +81,58 @@ 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 HandlerThread mNetworkCallbackHandlerThread;
private static boolean sNetworkConnected = false;
private static Network sNetwork = null;
- // TODO: Change this to a hashmap as there is only one provider per slot
- private static List<IwlanDataServiceProvider> sIwlanDataServiceProviderList =
- new ArrayList<IwlanDataServiceProvider>();
+ private static LinkProperties sLinkProperties = null;
+ @VisibleForTesting Handler mIwlanDataServiceHandler;
+ private HandlerThread mIwlanDataServiceHandlerThread;
+ private static final Map<Integer, IwlanDataServiceProvider> sIwlanDataServiceProviders =
+ new ConcurrentHashMap<>();
+ private static final int INVALID_SUB_ID = -1;
+
+ // The current subscription with the active internet PDN. Need not be the default data sub.
+ // If internet is over WiFi, this value will be INVALID_SUB_ID.
+ private static int mConnectedDataSub = INVALID_SUB_ID;
+
+ private static final int EVENT_BASE = IwlanEventListener.DATA_SERVICE_INTERNAL_EVENT_BASE;
+ private static final int EVENT_TUNNEL_OPENED = EVENT_BASE;
+ private static final int EVENT_TUNNEL_CLOSED = EVENT_BASE + 1;
+ private static final int EVENT_SETUP_DATA_CALL = EVENT_BASE + 2;
+ private static final int EVENT_DEACTIVATE_DATA_CALL = EVENT_BASE + 3;
+ private static final int EVENT_DATA_CALL_LIST_REQUEST = EVENT_BASE + 4;
+ private static final int EVENT_FORCE_CLOSE_TUNNEL = EVENT_BASE + 5;
+ private static final int EVENT_ADD_DATA_SERVICE_PROVIDER = EVENT_BASE + 6;
+ private static final int EVENT_REMOVE_DATA_SERVICE_PROVIDER = EVENT_BASE + 7;
+ private static final int EVENT_TUNNEL_OPENED_METRICS = EVENT_BASE + 8;
+ private static final int EVENT_TUNNEL_CLOSED_METRICS = EVENT_BASE + 9;
@VisibleForTesting
enum Transport {
UNSPECIFIED_NETWORK,
MOBILE,
- WIFI;
+ WIFI
}
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
static class IwlanNetworkMonitorCallback extends ConnectivityManager.NetworkCallback {
/** 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);
}
@@ -119,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);
}
@@ -128,41 +153,53 @@ 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);
}
/** 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)) {
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+
+ if (!network.equals(sNetwork)) {
+ Log.d(TAG, "Ignore LinkProperties changes for unused Network.");
+ return;
+ }
+
+ if (!linkProperties.equals(sLinkProperties)) {
+ 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);
if (networkCapabilities != null) {
if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
Log.d(TAG, "Network " + network + " connected using transport MOBILE");
+ IwlanDataService.setConnectedDataSub(getConnectedDataSub(networkCapabilities));
IwlanDataService.setNetworkConnected(true, network, Transport.MOBILE);
} else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
Log.d(TAG, "Network " + network + " connected using transport WIFI");
+ IwlanDataService.setConnectedDataSub(INVALID_SUB_ID);
IwlanDataService.setNetworkConnected(true, network, Transport.WIFI);
} else {
Log.w(TAG, "Network does not have cellular or wifi capability");
@@ -180,20 +217,20 @@ public class IwlanDataService extends DataService {
private final String SUB_TAG;
private final IwlanDataService mIwlanDataService;
private final IwlanTunnelCallback mIwlanTunnelCallback;
- private HandlerThread mHandlerThread;
- @VisibleForTesting Handler mHandler;
+ private final IwlanTunnelMetricsImpl mIwlanTunnelMetrics;
private boolean mWfcEnabled = false;
private boolean mCarrierConfigReady = false;
- private EpdgSelector mEpdgSelector;
- private IwlanDataTunnelStats mTunnelStats;
+ private final EpdgSelector mEpdgSelector;
+ private final IwlanDataTunnelStats mTunnelStats;
private CellInfo mCellInfo = null;
+ private int mCallState = TelephonyManager.CALL_STATE_IDLE;
+ private long mProcessingStartTime = 0;
// apn to TunnelState
- // Lock this at public entry and exit points if:
- // 1) the function changes mTunnelStateForApn
- // 2) Makes decisions based on contents of mTunnelStateForApn
- @GuardedBy("mTunnelStateForApn")
- private Map<String, TunnelState> mTunnelStateForApn = new ConcurrentHashMap<>();
+ // Access should be serialized inside IwlanDataServiceHandler
+ private final Map<String, TunnelState> mTunnelStateForApn = new ConcurrentHashMap<>();
+ private final Map<String, MetricsAtom> mMetricsAtomForApn = new ConcurrentHashMap<>();
+ private Calendar mCalendar;
// Holds the state of a tunnel (for an APN)
@VisibleForTesting
@@ -202,8 +239,8 @@ public class IwlanDataService extends DataService {
// this should be ideally be based on path MTU discovery. 1280 is the minimum packet
// size ipv6 routers have to handle so setting it to 1280 is the safest approach.
// ideally it should be 1280 - tunnelling overhead ?
- private static final int LINK_MTU =
- 1280; // TODO: need to substract tunnelling overhead?
+ private static final int LINK_MTU = 1280; // TODO: need to subtract tunnelling overhead?
+ private static final int LINK_MTU_CST = 1200; // Reserve 80 bytes for VCN.
static final int TUNNEL_DOWN = 1;
static final int TUNNEL_IN_BRINGUP = 2;
static final int TUNNEL_UP = 3;
@@ -216,6 +253,7 @@ public class IwlanDataService extends DataService {
private boolean mIsHandover;
private Date mBringUpStateTime = null;
private Date mUpStateTime = null;
+ private boolean mIsImsOrEmergency;
public int getPduSessionId() {
return mPduSessionId;
@@ -230,7 +268,11 @@ public class IwlanDataService extends DataService {
}
public int getLinkMtu() {
- return LINK_MTU; // TODO: need to substract tunnelling overhead
+ if ((sDefaultDataTransport == Transport.MOBILE) && sNetworkConnected) {
+ return LINK_MTU_CST;
+ } else {
+ return LINK_MTU; // TODO: need to subtract tunnelling overhead
+ }
}
public void setProtocolType(int protocolType) {
@@ -264,14 +306,16 @@ public class IwlanDataService extends DataService {
return mState;
}
- /** @param state (TunnelState.TUNNEL_DOWN|TUNNEL_UP|TUNNEL_DOWN) */
+ /**
+ * @param state (TunnelState.TUNNEL_DOWN|TUNNEL_UP|TUNNEL_DOWN)
+ */
public void setState(int state) {
mState = state;
if (mState == TunnelState.TUNNEL_IN_BRINGUP) {
- mBringUpStateTime = Calendar.getInstance().getTime();
+ mBringUpStateTime = mCalendar.getTime();
}
if (mState == TunnelState.TUNNEL_UP) {
- mUpStateTime = Calendar.getInstance().getTime();
+ mUpStateTime = mCalendar.getTime();
}
}
@@ -291,6 +335,18 @@ public class IwlanDataService extends DataService {
return mUpStateTime;
}
+ public Date getCurrentTime() {
+ return mCalendar.getTime();
+ }
+
+ public boolean getIsImsOrEmergency() {
+ return mIsImsOrEmergency;
+ }
+
+ public void setIsImsOrEmergency(boolean isImsOrEmergency) {
+ mIsImsOrEmergency = isImsOrEmergency;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -312,19 +368,23 @@ public class IwlanDataService extends DataService {
tunnelState = "IN FORCE CLEAN WAS IN BRINGUP";
break;
}
- sb.append("\tCurrent State of this tunnel: " + mState + " " + tunnelState);
- sb.append("\n\tTunnel state is in Handover: " + mIsHandover);
+ sb.append("\tCurrent State of this tunnel: ")
+ .append(mState)
+ .append(" ")
+ .append(tunnelState);
+ sb.append("\n\tTunnel state is in Handover: ").append(mIsHandover);
if (mBringUpStateTime != null) {
- sb.append("\n\tTunnel bring up initiated at: " + mBringUpStateTime);
+ sb.append("\n\tTunnel bring up initiated at: ").append(mBringUpStateTime);
} else {
sb.append("\n\tPotential leak. Null mBringUpStateTime");
}
if (mUpStateTime != null) {
- sb.append("\n\tTunnel is up at: " + mUpStateTime);
+ sb.append("\n\tTunnel is up at: ").append(mUpStateTime);
}
if (mUpStateTime != null && mBringUpStateTime != null) {
long tunnelUpTime = mUpStateTime.getTime() - mBringUpStateTime.getTime();
- sb.append("\n\tTime taken for the tunnel to come up in ms: " + tunnelUpTime);
+ sb.append("\n\tTime taken for the tunnel to come up in ms: ")
+ .append(tunnelUpTime);
}
return sb.toString();
}
@@ -333,10 +393,10 @@ public class IwlanDataService extends DataService {
@VisibleForTesting
class IwlanTunnelCallback implements EpdgTunnelManager.TunnelCallback {
- DataServiceProvider mDataServiceProvider;
+ IwlanDataServiceProvider mIwlanDataServiceProvider;
- public IwlanTunnelCallback(DataServiceProvider dsp) {
- mDataServiceProvider = dsp;
+ public IwlanTunnelCallback(IwlanDataServiceProvider dsp) {
+ mIwlanDataServiceProvider = dsp;
}
// TODO: full implementation
@@ -345,154 +405,30 @@ public class IwlanDataService extends DataService {
Log.d(
SUB_TAG,
"Tunnel opened!. APN: " + apnName + "linkproperties: " + linkProperties);
- synchronized (mTunnelStateForApn) {
- TunnelState tunnelState = mTunnelStateForApn.get(apnName);
- // tunnelstate should not be null, design violation.
- // if its null, we should crash and debug.
- tunnelState.setTunnelLinkProperties(linkProperties);
- tunnelState.setState(TunnelState.TUNNEL_UP);
- mTunnelStats.reportTunnelSetupSuccess(apnName, tunnelState);
-
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- tunnelState.getDataServiceCallback(),
- apnTunnelStateToDataCallResponse(apnName));
- }
+ 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.
- synchronized (mTunnelStateForApn) {
- TunnelState tunnelState = mTunnelStateForApn.get(apnName);
- mTunnelStats.reportTunnelDown(apnName, tunnelState);
- mTunnelStateForApn.remove(apnName);
-
- if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGUP
- || tunnelState.getState()
- == TunnelState.TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP) {
- DataCallResponse.Builder respBuilder = new DataCallResponse.Builder();
- respBuilder
- .setId(apnName.hashCode())
- .setProtocolType(tunnelState.getProtocolType());
-
- if (tunnelState.getIsHandover()) {
- respBuilder.setHandoverFailureMode(
- DataCallResponse
- .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
- } else {
- respBuilder.setHandoverFailureMode(
- DataCallResponse
- .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
- }
-
- if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGUP) {
- respBuilder.setCause(
- ErrorPolicyManager.getInstance(mContext, getSlotIndex())
- .getDataFailCause(apnName));
- respBuilder.setRetryDurationMillis(
- ErrorPolicyManager.getInstance(mContext, getSlotIndex())
- .getCurrentRetryTimeMs(apnName));
- } else if (tunnelState.getState()
- == TunnelState.TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP) {
- respBuilder.setCause(DataFailCause.IWLAN_NETWORK_FAILURE);
- respBuilder.setRetryDurationMillis(5000);
- }
-
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- tunnelState.getDataServiceCallback(),
- respBuilder.build());
- return;
- }
-
- // iwlan service triggered teardown
- if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGDOWN) {
-
- // IO exception happens when IKE library fails to retransmit requests.
- // This can happen for multiple reasons:
- // 1. Network disconnection due to wifi off.
- // 2. Epdg server does not respond.
- // 3. Socket send/receive fails.
- // Ignore this during tunnel bring down.
- if (error.getErrorType() != IwlanError.NO_ERROR
- && error.getErrorType() != IwlanError.IKE_INTERNAL_IO_EXCEPTION) {
- Log.e(SUB_TAG, "Unexpected error during tunnel bring down: " + error);
- }
-
- deliverCallback(
- CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- tunnelState.getDataServiceCallback(),
- null);
-
- return;
- }
-
- // just update list of data calls. No way to send error up
- notifyDataCallListChanged(getCallList());
- }
- }
- }
-
- private final class DSPHandler extends Handler {
- private final String TAG =
- IwlanDataService.class.getSimpleName()
- + DSPHandler.class.getSimpleName()
- + getSlotIndex();
-
- @Override
- public void handleMessage(Message msg) {
- Log.d(TAG, "msg.what = " + msg.what);
- switch (msg.what) {
- case IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT:
- Log.d(TAG, "On CARRIER_CONFIG_CHANGED_EVENT");
- mCarrierConfigReady = true;
- dnsPrefetchCheck();
- break;
- case IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT:
- Log.d(TAG, "On CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT");
- mCarrierConfigReady = false;
- break;
- case IwlanEventListener.WIFI_CALLING_ENABLE_EVENT:
- Log.d(TAG, "On WIFI_CALLING_ENABLE_EVENT");
- mWfcEnabled = true;
- dnsPrefetchCheck();
- break;
- case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT:
- Log.d(TAG, "On WIFI_CALLING_DISABLE_EVENT");
- mWfcEnabled = false;
- break;
- case IwlanEventListener.CELLINFO_CHANGED_EVENT:
- Log.d(TAG, "On CELLINFO_CHANGED_EVENT");
- List<CellInfo> cellInfolist = (List<CellInfo>) msg.obj;
-
- if (cellInfolist != null && isRegisteredCellInfoChanged(cellInfolist)) {
- int[] addrResolutionMethods =
- IwlanHelper.getConfig(
- CarrierConfigManager.Iwlan
- .KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
- mContext,
- getSlotIndex());
- for (int addrResolutionMethod : addrResolutionMethods) {
- if (addrResolutionMethod
- == CarrierConfigManager.Iwlan.EPDG_ADDRESS_CELLULAR_LOC) {
- dnsPrefetchCheck();
- }
- }
- }
- break;
- default:
- Log.d(TAG, "Unknown message received!");
- break;
- }
- }
-
- DSPHandler(Looper looper) {
- super(looper);
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(
+ EVENT_TUNNEL_CLOSED,
+ new TunnelClosedData(
+ apnName,
+ error,
+ mIwlanDataServiceProvider)));
}
}
@@ -522,10 +458,9 @@ public class IwlanDataService extends DataService {
private long statCount;
private final long COUNT_MAX = 1000;
- private final int APN_COUNT_MAX = 10;
public IwlanDataTunnelStats() {
- mStartTime = Calendar.getInstance().getTime();
+ mStartTime = mCalendar.getTime();
statCount = 0L;
}
@@ -566,7 +501,7 @@ public class IwlanDataService extends DataService {
}
// Unsolicited tunnel down as tunnel has to be in BRINGDOWN if
- // there is a deactivate call associated with this.
+ // there is a deactivateDataCall() associated with this.
if (tunnelState.getState() == TunnelState.TUNNEL_UP) {
if (!mUnsolTunnelDownCounts.containsKey(apn)) {
mUnsolTunnelDownCounts.put(apn, 0L);
@@ -574,7 +509,7 @@ public class IwlanDataService extends DataService {
long count = mUnsolTunnelDownCounts.get(apn);
mUnsolTunnelDownCounts.put(apn, ++count);
}
- Date currentTime = Calendar.getInstance().getTime();
+ Date currentTime = tunnelState.getCurrentTime();
Date upTime = tunnelState.getUpStateTime();
if (upTime != null) {
if (!mTunnelUpStats.containsKey(apn)) {
@@ -587,48 +522,46 @@ public class IwlanDataService extends DataService {
}
boolean maxApnReached() {
- if (mTunnelSetupSuccessStats.size() >= APN_COUNT_MAX
+ int APN_COUNT_MAX = 10;
+ return mTunnelSetupSuccessStats.size() >= APN_COUNT_MAX
|| mTunnelSetupFailureCounts.size() >= APN_COUNT_MAX
|| mUnsolTunnelDownCounts.size() >= APN_COUNT_MAX
- || mTunnelUpStats.size() >= APN_COUNT_MAX) {
- return true;
- }
- return false;
+ || mTunnelUpStats.size() >= APN_COUNT_MAX;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("IwlanDataTunnelStats:");
- sb.append("\n\tmStartTime: " + mStartTime);
+ sb.append("\n\tmStartTime: ").append(mStartTime);
sb.append("\n\ttunnelSetupSuccessStats:");
for (Map.Entry<String, LongSummaryStatistics> entry :
mTunnelSetupSuccessStats.entrySet()) {
- sb.append("\n\t Apn: " + entry.getKey());
- sb.append("\n\t " + entry.getValue());
+ sb.append("\n\t Apn: ").append(entry.getKey());
+ sb.append("\n\t ").append(entry.getValue());
}
sb.append("\n\ttunnelUpStats:");
for (Map.Entry<String, LongSummaryStatistics> entry : mTunnelUpStats.entrySet()) {
- sb.append("\n\t Apn: " + entry.getKey());
- sb.append("\n\t " + entry.getValue());
+ sb.append("\n\t Apn: ").append(entry.getKey());
+ sb.append("\n\t ").append(entry.getValue());
}
sb.append("\n\ttunnelSetupFailureCounts: ");
for (Map.Entry<String, Long> entry : mTunnelSetupFailureCounts.entrySet()) {
- sb.append("\n\t Apn: " + entry.getKey());
- sb.append("\n\t counts: " + entry.getValue());
+ sb.append("\n\t Apn: ").append(entry.getKey());
+ sb.append("\n\t counts: ").append(entry.getValue());
}
sb.append("\n\tunsolTunnelDownCounts: ");
for (Map.Entry<String, Long> entry : mTunnelSetupFailureCounts.entrySet()) {
- sb.append("\n\t Apn: " + entry.getKey());
- sb.append("\n\t counts: " + entry.getValue());
+ sb.append("\n\t Apn: ").append(entry.getKey());
+ sb.append("\n\t counts: ").append(entry.getValue());
}
- sb.append("\n\tendTime: " + Calendar.getInstance().getTime());
+ sb.append("\n\tendTime: ").append(mCalendar.getTime());
return sb.toString();
}
private void reset() {
- mStartTime = Calendar.getInstance().getTime();
+ mStartTime = mCalendar.getTime();
mTunnelSetupSuccessStats = new HashMap<String, LongSummaryStatistics>();
mTunnelUpStats = new HashMap<String, LongSummaryStatistics>();
mTunnelSetupFailureCounts = new HashMap<String, Long>();
@@ -637,12 +570,6 @@ public class IwlanDataService extends DataService {
}
}
- Looper getLooper() {
- mHandlerThread = new HandlerThread("DSPHandlerThread");
- mHandlerThread.start();
- return mHandlerThread.getLooper();
- }
-
/**
* Constructor
*
@@ -657,26 +584,25 @@ public class IwlanDataService extends DataService {
// get reference to resolver
mIwlanDataService = iwlanDataService;
mIwlanTunnelCallback = new IwlanTunnelCallback(this);
+ mIwlanTunnelMetrics = new IwlanTunnelMetricsImpl(this, getIwlanDataServiceHandler());
mEpdgSelector = EpdgSelector.getSelectorInstance(mContext, slotIndex);
+ mCalendar = Calendar.getInstance();
mTunnelStats = new IwlanDataTunnelStats();
// Register IwlanEventListener
- initHandler();
List<Integer> events = new ArrayList<Integer>();
events.add(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT);
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);
- IwlanEventListener.getInstance(mContext, slotIndex).addEventListener(events, mHandler);
- }
-
- void initHandler() {
- mHandler = new DSPHandler(getLooper());
+ events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT);
+ IwlanEventListener.getInstance(mContext, slotIndex)
+ .addEventListener(events, getIwlanDataServiceHandler());
}
- @VisibleForTesting
- EpdgTunnelManager getTunnelManager() {
+ private EpdgTunnelManager getTunnelManager() {
return EpdgTunnelManager.getInstance(mContext, getSlotIndex());
}
@@ -693,23 +619,30 @@ public class IwlanDataService extends DataService {
.setProtocolType(tunnelState.getProtocolType())
.setCause(DataFailCause.NONE);
- if (tunnelState.getState() != TunnelState.TUNNEL_UP) {
- // no need to fill additional params
- return responseBuilder.setLinkStatus(DataCallResponse.LINK_STATUS_UNKNOWN).build();
+ responseBuilder.setLinkStatus(DataCallResponse.LINK_STATUS_INACTIVE);
+ int state = tunnelState.getState();
+
+ if (state == TunnelState.TUNNEL_UP) {
+ responseBuilder.setLinkStatus(DataCallResponse.LINK_STATUS_ACTIVE);
+ }
+
+ TunnelLinkProperties tunnelLinkProperties = tunnelState.getTunnelLinkProperties();
+ if (tunnelLinkProperties == null) {
+ Log.d(TAG, "PDN with empty linkproperties. TunnelState : " + state);
+ return responseBuilder.build();
}
// fill wildcard address for gatewayList (used by DataConnection to add routes)
List<InetAddress> gatewayList = new ArrayList<>();
- List<LinkAddress> linkAddrList =
- tunnelState.getTunnelLinkProperties().internalAddresses();
- if (linkAddrList.stream().anyMatch(t -> t.isIpv4())) {
+ List<LinkAddress> linkAddrList = tunnelLinkProperties.internalAddresses();
+ if (linkAddrList.stream().anyMatch(LinkAddress::isIpv4)) {
try {
gatewayList.add(Inet4Address.getByName("0.0.0.0"));
} catch (UnknownHostException e) {
// should never happen for static string 0.0.0.0
}
}
- if (linkAddrList.stream().anyMatch(t -> t.isIpv6())) {
+ if (linkAddrList.stream().anyMatch(LinkAddress::isIpv6)) {
try {
gatewayList.add(Inet6Address.getByName("::"));
} catch (UnknownHostException e) {
@@ -717,18 +650,16 @@ public class IwlanDataService extends DataService {
}
}
- if (tunnelState.getTunnelLinkProperties().sliceInfo().isPresent()) {
- responseBuilder.setSliceInfo(
- tunnelState.getTunnelLinkProperties().sliceInfo().get());
+ if (tunnelLinkProperties.sliceInfo().isPresent()) {
+ responseBuilder.setSliceInfo(tunnelLinkProperties.sliceInfo().get());
}
return responseBuilder
.setAddresses(linkAddrList)
- .setDnsAddresses(tunnelState.getTunnelLinkProperties().dnsAddresses())
- .setPcscfAddresses(tunnelState.getTunnelLinkProperties().pcscfAddresses())
- .setInterfaceName(tunnelState.getTunnelLinkProperties().ifaceName())
+ .setDnsAddresses(tunnelLinkProperties.dnsAddresses())
+ .setPcscfAddresses(tunnelLinkProperties.pcscfAddresses())
+ .setInterfaceName(tunnelLinkProperties.ifaceName())
.setGatewayAddresses(gatewayList)
- .setLinkStatus(DataCallResponse.LINK_STATUS_ACTIVE)
.setMtu(tunnelState.getLinkMtu())
.setMtuV4(tunnelState.getLinkMtu())
.setMtuV6(tunnelState.getLinkMtu())
@@ -827,6 +758,7 @@ public class IwlanDataService extends DataService {
boolean matchAllRuleAllowed,
@NonNull DataServiceCallback callback) {
+ mProcessingStartTime = System.currentTimeMillis();
Log.d(
SUB_TAG,
"Setup data call with network: "
@@ -844,128 +776,50 @@ public class IwlanDataService extends DataService {
+ ", pduSessionId: "
+ pduSessionId);
- // Framework will never call bringup on the same APN back 2 back.
- // but add a safety check
- if ((accessNetworkType != AccessNetworkType.IWLAN)
- || (dataProfile == null)
- || (linkProperties == null && reason == DataService.REQUEST_REASON_HANDOVER)) {
-
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_ERROR_INVALID_ARG,
- callback,
- null);
- return;
- }
-
- synchronized (mTunnelStateForApn) {
- boolean isDDS = IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex());
- boolean isCSTEnabled =
- IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex());
- boolean networkConnected = isNetworkConnected(isDDS, isCSTEnabled);
- Log.d(
- SUB_TAG,
- "isDds: "
- + isDDS
- + ", isCstEnabled: "
- + isCSTEnabled
- + ", transport: "
- + sDefaultDataTransport);
-
- if (networkConnected == false) {
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- 5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
+ SetupDataCallData setupDataCallData =
+ new SetupDataCallData(
+ accessNetworkType,
+ dataProfile,
+ isRoaming,
+ allowRoaming,
+ reason,
+ linkProperties,
+ pduSessionId,
+ sliceInfo,
+ trafficDescriptor,
+ matchAllRuleAllowed,
callback,
- null);
- return;
- }
-
- TunnelState tunnelState = mTunnelStateForApn.get(dataProfile.getApn());
-
- // Return the existing PDN if the pduSessionId is the same and the tunnel state is
- // TUNNEL_UP.
- if (tunnelState != null) {
- if (tunnelState.getPduSessionId() == pduSessionId
- && tunnelState.getState() == TunnelState.TUNNEL_UP) {
- Log.w(
- SUB_TAG,
- "The tunnel for " + dataProfile.getApn() + " already exists.");
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- callback,
- apnTunnelStateToDataCallResponse(dataProfile.getApn()));
- return;
- } else {
- Log.e(
- SUB_TAG,
- "Force close the existing PDN. pduSessionId = "
- + tunnelState.getPduSessionId()
- + " Tunnel State = "
- + tunnelState.getState());
- getTunnelManager().closeTunnel(dataProfile.getApn(), true /* forceClose */);
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- 5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
- callback,
- null);
- return;
- }
- }
+ this);
- TunnelSetupRequest.Builder tunnelReqBuilder =
- TunnelSetupRequest.builder()
- .setApnName(dataProfile.getApn())
- .setNetwork(sNetwork)
- .setIsRoaming(isRoaming)
- .setPduSessionId(pduSessionId)
- .setApnIpProtocol(
- isRoaming
- ? dataProfile.getRoamingProtocolType()
- : dataProfile.getProtocolType());
-
- if (reason == DataService.REQUEST_REASON_HANDOVER) {
- // for now assume that, at max, only one address of eachtype (v4/v6).
- // TODO: Check if multiple ips can be sent in ike tunnel setup
- for (LinkAddress lAddr : linkProperties.getLinkAddresses()) {
- if (lAddr.isIpv4()) {
- tunnelReqBuilder.setSrcIpv4Address(lAddr.getAddress());
- } else if (lAddr.isIpv6()) {
- tunnelReqBuilder.setSrcIpv6Address(lAddr.getAddress());
- tunnelReqBuilder.setSrcIpv6AddressPrefixLength(lAddr.getPrefixLength());
- }
- }
- }
+ int networkTransport = -1;
+ if (sDefaultDataTransport == Transport.MOBILE) {
+ networkTransport = TRANSPORT_CELLULAR;
+ } else if (sDefaultDataTransport == Transport.WIFI) {
+ networkTransport = TRANSPORT_WIFI;
+ }
- int apnTypeBitmask = dataProfile.getSupportedApnTypesBitmask();
- boolean isIMS = (apnTypeBitmask & ApnSetting.TYPE_IMS) == ApnSetting.TYPE_IMS;
- boolean isEmergency =
- (apnTypeBitmask & ApnSetting.TYPE_EMERGENCY) == ApnSetting.TYPE_EMERGENCY;
- tunnelReqBuilder.setRequestPcscf(isIMS || isEmergency);
- tunnelReqBuilder.setIsEmergency(isEmergency);
-
- setTunnelState(
- dataProfile,
- callback,
- TunnelState.TUNNEL_IN_BRINGUP,
- null,
+ if (dataProfile != null) {
+ this.setMetricsAtom(
+ // ApnName
+ dataProfile.getApnSetting().getApnName(),
+ // ApnType
+ dataProfile.getApnSetting().getApnTypeBitmask(),
+ // IsHandover
(reason == DataService.REQUEST_REASON_HANDOVER),
- pduSessionId);
-
- boolean result =
- getTunnelManager()
- .bringUpTunnel(tunnelReqBuilder.build(), getIwlanTunnelCallback());
- Log.d(SUB_TAG, "bringup Tunnel with result:" + result);
- if (!result) {
- deliverCallback(
- CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_ERROR_INVALID_ARG,
- callback,
- null);
- return;
- }
+ // Source Rat
+ getCurrentCellularRat(),
+ // IsRoaming
+ isRoaming,
+ // Is Network Connected
+ sNetworkConnected,
+ // Transport Type
+ networkTransport);
}
+
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(EVENT_SETUP_DATA_CALL, setupDataCallData));
}
/**
@@ -992,49 +846,38 @@ public class IwlanDataService extends DataService {
+ "callback: "
+ callback);
- synchronized (mTunnelStateForApn) {
- for (String apnName : mTunnelStateForApn.keySet()) {
- if (apnName.hashCode() == cid) {
- /*
- No need to check state since dataconnection in framework serializes
- setup and deactivate calls using callId/cid.
- */
- mTunnelStateForApn.get(apnName).setState(TunnelState.TUNNEL_IN_BRINGDOWN);
- mTunnelStateForApn.get(apnName).setDataServiceCallback(callback);
- boolean isConnected =
- isNetworkConnected(
- IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
- IwlanHelper.isCrossSimCallingEnabled(
- mContext, getSlotIndex()));
- getTunnelManager().closeTunnel(apnName, !isConnected);
- return;
- }
- }
+ DeactivateDataCallData deactivateDataCallData =
+ new DeactivateDataCallData(cid, reason, callback, this);
- deliverCallback(
- CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE,
- DataServiceCallback.RESULT_ERROR_INVALID_ARG,
- callback,
- null);
- }
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(
+ EVENT_DEACTIVATE_DATA_CALL, deactivateDataCallData));
}
public void forceCloseTunnelsInDeactivatingState() {
- synchronized (mTunnelStateForApn) {
- for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
- TunnelState tunnelState = entry.getValue();
- if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGDOWN) {
- getTunnelManager().closeTunnel(entry.getKey(), true);
- }
+ for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
+ TunnelState tunnelState = entry.getValue();
+ if (tunnelState.getState() == TunnelState.TUNNEL_IN_BRINGDOWN) {
+ getTunnelManager()
+ .closeTunnel(
+ entry.getKey(),
+ true /* forceClose */,
+ getIwlanTunnelCallback(),
+ getIwlanTunnelMetrics());
}
}
}
void forceCloseTunnels() {
- synchronized (mTunnelStateForApn) {
- for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
- getTunnelManager().closeTunnel(entry.getKey(), true);
- }
+ for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
+ getTunnelManager()
+ .closeTunnel(
+ entry.getKey(),
+ true /* forceClose */,
+ getIwlanTunnelCallback(),
+ getIwlanTunnelMetrics());
}
}
@@ -1045,11 +888,13 @@ public class IwlanDataService extends DataService {
*/
@Override
public void requestDataCallList(DataServiceCallback callback) {
- deliverCallback(
- CALLBACK_TYPE_GET_DATACALL_LIST_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- callback,
- null);
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(
+ EVENT_DATA_CALL_LIST_REQUEST,
+ new DataCallRequestData(
+ callback, IwlanDataServiceProvider.this)));
}
@VisibleForTesting
@@ -1059,14 +904,41 @@ public class IwlanDataService extends DataService {
int tunnelStatus,
TunnelLinkProperties linkProperties,
boolean isHandover,
- int pduSessionId) {
+ int pduSessionId,
+ boolean isImsOrEmergency) {
TunnelState tunnelState = new TunnelState(callback);
tunnelState.setState(tunnelStatus);
- tunnelState.setProtocolType(dataProfile.getProtocolType());
+ tunnelState.setProtocolType(dataProfile.getApnSetting().getProtocol());
tunnelState.setTunnelLinkProperties(linkProperties);
tunnelState.setIsHandover(isHandover);
tunnelState.setPduSessionId(pduSessionId);
- mTunnelStateForApn.put(dataProfile.getApn(), tunnelState);
+ tunnelState.setIsImsOrEmergency(isImsOrEmergency);
+ mTunnelStateForApn.put(dataProfile.getApnSetting().getApnName(), tunnelState);
+ }
+
+ @VisibleForTesting
+ void setMetricsAtom(
+ String apnName,
+ int apntype,
+ boolean isHandover,
+ int sourceRat,
+ boolean isRoaming,
+ boolean isNetworkConnected,
+ int transportType) {
+ MetricsAtom metricsAtom = new MetricsAtom();
+ metricsAtom.setApnType(apntype);
+ metricsAtom.setIsHandover(isHandover);
+ metricsAtom.setSourceRat(sourceRat);
+ metricsAtom.setIsCellularRoaming(isRoaming);
+ metricsAtom.setIsNetworkConnected(isNetworkConnected);
+ metricsAtom.setTransportType(transportType);
+ mMetricsAtomForApn.put(apnName, metricsAtom);
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public MetricsAtom getMetricsAtomByApn(String apnName) {
+ return mMetricsAtomForApn.get(apnName);
}
@VisibleForTesting
@@ -1075,31 +947,40 @@ public class IwlanDataService extends DataService {
}
@VisibleForTesting
+ public IwlanTunnelMetricsImpl getIwlanTunnelMetrics() {
+ return mIwlanTunnelMetrics;
+ }
+
+ @VisibleForTesting
IwlanDataTunnelStats getTunnelStats() {
return mTunnelStats;
}
- private void updateNetwork(Network network) {
- if (network != null) {
- synchronized (mTunnelStateForApn) {
- 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);
- } else {
- if (mIwlanDataService.isNetworkConnected(
- IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
- IwlanHelper.isCrossSimCallingEnabled(
- mContext, getSlotIndex()))) {
- getTunnelManager().updateNetwork(network, entry.getKey());
- }
- }
- }
+ 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());
}
}
}
@@ -1122,34 +1003,91 @@ public class IwlanDataService extends DataService {
private void dnsPrefetchCheck() {
boolean networkConnected =
mIwlanDataService.isNetworkConnected(
- IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
+ isActiveDataOnOtherSub(getSlotIndex()),
IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()));
/* Check if we need to do prefecting */
- synchronized (mTunnelStateForApn) {
- if (networkConnected == true
- && mCarrierConfigReady == true
- && mWfcEnabled == true
- && mTunnelStateForApn.isEmpty()) {
-
- // Get roaming status
- TelephonyManager telephonyManager =
- mContext.getSystemService(TelephonyManager.class);
- telephonyManager =
- telephonyManager.createForSubscriptionId(
- IwlanHelper.getSubId(mContext, getSlotIndex()));
- boolean isRoaming = telephonyManager.isNetworkRoaming();
- Log.d(TAG, "Trigger EPDG prefetch. Roaming=" + isRoaming);
-
- prefetchEpdgServerList(mIwlanDataService.sNetwork, isRoaming);
- }
+ if (networkConnected
+ && mCarrierConfigReady
+ && mWfcEnabled
+ && mTunnelStateForApn.isEmpty()) {
+
+ // Get roaming status
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ telephonyManager =
+ telephonyManager.createForSubscriptionId(
+ IwlanHelper.getSubId(mContext, getSlotIndex()));
+ boolean isRoaming = telephonyManager.isNetworkRoaming();
+ Log.d(TAG, "Trigger EPDG prefetch. Roaming=" + isRoaming);
+
+ prefetchEpdgServerList(mIwlanDataService.sNetwork, isRoaming);
}
}
private void prefetchEpdgServerList(Network network, boolean isRoaming) {
mEpdgSelector.getValidatedServerList(
- 0, EpdgSelector.PROTO_FILTER_IPV4V6, isRoaming, false, network, null);
+ 0,
+ EpdgSelector.PROTO_FILTER_IPV4V6,
+ EpdgSelector.SYSTEM_PREFERRED,
+ isRoaming,
+ false,
+ network,
+ null);
mEpdgSelector.getValidatedServerList(
- 0, EpdgSelector.PROTO_FILTER_IPV4V6, isRoaming, true, network, null);
+ 0,
+ EpdgSelector.PROTO_FILTER_IPV4V6,
+ EpdgSelector.SYSTEM_PREFERRED,
+ isRoaming,
+ true,
+ network,
+ null);
+ }
+
+ private int getCurrentCellularRat() {
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ telephonyManager =
+ telephonyManager.createForSubscriptionId(
+ IwlanHelper.getSubId(mContext, getSlotIndex()));
+ List<CellInfo> cellInfoList = telephonyManager.getAllCellInfo();
+ if (cellInfoList == null) {
+ Log.e(TAG, "cellInfoList is NULL");
+ return 0;
+ }
+
+ for (CellInfo cellInfo : cellInfoList) {
+ if (!cellInfo.isRegistered()) {
+ continue;
+ }
+ if (cellInfo instanceof CellInfoGsm) {
+ return TelephonyManager.NETWORK_TYPE_GSM;
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ return TelephonyManager.NETWORK_TYPE_UMTS;
+ } else if (cellInfo instanceof CellInfoLte) {
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ } else if (cellInfo instanceof CellInfoNr) {
+ return TelephonyManager.NETWORK_TYPE_NR;
+ }
+ }
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ /* Determines if this subscription is in an active call */
+ private boolean isOnCall() {
+ return mCallState != TelephonyManager.CALL_STATE_IDLE;
+ }
+
+ /**
+ * IMS and Emergency are not allowed to retry with initial attach during call to keep call
+ * continuity. Other APNs like XCAP and MMS are allowed to retry with initial attach
+ * regardless of the call state.
+ */
+ private boolean shouldRetryWithInitialAttachForHandoverRequest(
+ String apn, TunnelState tunnelState) {
+ boolean isOnImsOrEmergencyCall = tunnelState.getIsImsOrEmergency() && isOnCall();
+ return tunnelState.getIsHandover()
+ && !isOnImsOrEmergencyCall
+ && ErrorPolicyManager.getInstance(mContext, getSlotIndex())
+ .shouldRetryWithInitialAttach(apn);
}
/**
@@ -1160,109 +1098,801 @@ public class IwlanDataService extends DataService {
public void close() {
// TODO: call epdgtunnelmanager.releaseInstance or equivalent
mIwlanDataService.removeDataServiceProvider(this);
- IwlanEventListener.getInstance(mContext, getSlotIndex()).removeEventListener(mHandler);
- mHandlerThread.quit();
+ IwlanEventListener iwlanEventListener =
+ IwlanEventListener.getInstance(mContext, getSlotIndex());
+ iwlanEventListener.removeEventListener(getIwlanDataServiceHandler());
+ iwlanEventListener.unregisterContentObserver();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("---- IwlanDataServiceProvider[" + getSlotIndex() + "] ----");
boolean isDDS = IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex());
boolean isCSTEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex());
- pw.println("isDefaultDataSub: " + isDDS + " isCrossSimEnabled: " + isCSTEnabled);
+ pw.println(
+ "isDefaultDataSlot: "
+ + isDDS
+ + "subID: "
+ + IwlanHelper.getSubId(mContext, getSlotIndex())
+ + " mConnectedDataSub: "
+ + mConnectedDataSub
+ + " isCrossSimEnabled: "
+ + isCSTEnabled);
pw.println(
"isNetworkConnected: "
- + isNetworkConnected(isDDS, isCSTEnabled)
+ + isNetworkConnected(
+ isActiveDataOnOtherSub(getSlotIndex()), isCSTEnabled)
+ " Wfc enabled: "
+ mWfcEnabled);
- synchronized (mTunnelStateForApn) {
- for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
- pw.println("Tunnel state for APN: " + entry.getKey());
- pw.println(entry.getValue());
- }
+ for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
+ pw.println("Tunnel state for APN: " + entry.getKey());
+ pw.println(entry.getValue());
}
pw.println(mTunnelStats);
- EpdgTunnelManager.getInstance(mContext, getSlotIndex()).dump(fd, pw, args);
- ErrorPolicyManager.getInstance(mContext, getSlotIndex()).dump(fd, pw, args);
+ EpdgTunnelManager.getInstance(mContext, getSlotIndex()).dump(pw);
+ ErrorPolicyManager.getInstance(mContext, getSlotIndex()).dump(pw);
pw.println("-------------------------------------");
}
+
+ @VisibleForTesting
+ public void setCalendar(Calendar c) {
+ mCalendar = c;
+ }
+ }
+
+ private final class IwlanDataServiceHandler extends Handler {
+ private final String TAG = IwlanDataServiceHandler.class.getSimpleName();
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "msg.what = " + eventToString(msg.what));
+
+ String apnName;
+ IwlanDataServiceProvider iwlanDataServiceProvider;
+ IwlanDataServiceProvider.TunnelState tunnelState;
+ DataServiceCallback callback;
+ int reason;
+ int slotId;
+ int retryTimeMillis;
+ int errorCause;
+ MetricsAtom metricsAtom;
+
+ switch (msg.what) {
+ case EVENT_TUNNEL_OPENED:
+ TunnelOpenedData tunnelOpenedData = (TunnelOpenedData) msg.obj;
+ iwlanDataServiceProvider = tunnelOpenedData.mIwlanDataServiceProvider;
+ apnName = tunnelOpenedData.mApnName;
+ TunnelLinkProperties tunnelLinkProperties =
+ tunnelOpenedData.mTunnelLinkProperties;
+
+ tunnelState = iwlanDataServiceProvider.mTunnelStateForApn.get(apnName);
+ // tunnelstate should not be null, design violation.
+ // if its null, we should crash and debug.
+ tunnelState.setTunnelLinkProperties(tunnelLinkProperties);
+ tunnelState.setState(IwlanDataServiceProvider.TunnelState.TUNNEL_UP);
+ iwlanDataServiceProvider.mTunnelStats.reportTunnelSetupSuccess(
+ apnName, tunnelState);
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ tunnelState.getDataServiceCallback(),
+ iwlanDataServiceProvider.apnTunnelStateToDataCallResponse(apnName));
+ break;
+
+ case EVENT_TUNNEL_CLOSED:
+ TunnelClosedData tunnelClosedData = (TunnelClosedData) msg.obj;
+ iwlanDataServiceProvider = tunnelClosedData.mIwlanDataServiceProvider;
+ apnName = tunnelClosedData.mApnName;
+ IwlanError iwlanError = tunnelClosedData.mIwlanError;
+
+ tunnelState = iwlanDataServiceProvider.mTunnelStateForApn.get(apnName);
+
+ if (tunnelState == null) {
+ // On a successful handover to EUTRAN, the NW may initiate an IKE DEL before
+ // the UE initiates a deactivateDataCall(). There may be a race condition
+ // where the deactivateDataCall() arrives immediately before
+ // IwlanDataService receives EVENT_TUNNEL_CLOSED (and clears TunnelState).
+ // Even though there is no tunnel, EpdgTunnelManager will still process the
+ // bringdown request and send back an onClosed() to ensure state coherence.
+ if (iwlanError.getErrorType() != IwlanError.TUNNEL_NOT_FOUND) {
+ Log.w(
+ TAG,
+ "Tunnel state does not exist! Unexpected IwlanError: "
+ + iwlanError);
+ }
+ break;
+ }
+
+ iwlanDataServiceProvider.mTunnelStats.reportTunnelDown(apnName, tunnelState);
+ iwlanDataServiceProvider.mTunnelStateForApn.remove(apnName);
+ metricsAtom = iwlanDataServiceProvider.mMetricsAtomForApn.get(apnName);
+
+ if (tunnelState.getState()
+ == IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP
+ || tunnelState.getState()
+ == IwlanDataServiceProvider.TunnelState
+ .TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP) {
+ DataCallResponse.Builder respBuilder = new DataCallResponse.Builder();
+ respBuilder
+ .setId(apnName.hashCode())
+ .setProtocolType(tunnelState.getProtocolType());
+
+ if (iwlanDataServiceProvider.shouldRetryWithInitialAttachForHandoverRequest(
+ apnName, tunnelState)) {
+ respBuilder.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ metricsAtom.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ } else if (tunnelState.getIsHandover()) {
+ respBuilder.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
+ metricsAtom.setHandoverFailureMode(
+ DataCallResponse
+ .HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
+ }
+
+ errorCause =
+ ErrorPolicyManager.getInstance(
+ mContext, iwlanDataServiceProvider.getSlotIndex())
+ .getDataFailCause(apnName);
+ if (errorCause != DataFailCause.NONE) {
+ respBuilder.setCause(errorCause);
+ metricsAtom.setDataCallFailCause(errorCause);
+
+ retryTimeMillis =
+ (int)
+ ErrorPolicyManager.getInstance(
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex())
+ .getCurrentRetryTimeMs(apnName);
+ respBuilder.setRetryDurationMillis(retryTimeMillis);
+ metricsAtom.setRetryDurationMillis(retryTimeMillis);
+ } else {
+ // TODO(b/265215349): Use a different DataFailCause for scenario where
+ // tunnel in bringup is closed or force-closed without error.
+ respBuilder.setCause(DataFailCause.IWLAN_NETWORK_FAILURE);
+ metricsAtom.setDataCallFailCause(DataFailCause.IWLAN_NETWORK_FAILURE);
+ respBuilder.setRetryDurationMillis(5000);
+ metricsAtom.setRetryDurationMillis(5000);
+ }
+
+ // 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);
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ tunnelState.getDataServiceCallback(),
+ respBuilder.build());
+ return;
+ }
+
+ // iwlan service triggered teardown
+ if (tunnelState.getState()
+ == IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGDOWN) {
+
+ // IO exception happens when IKE library fails to retransmit requests.
+ // This can happen for multiple reasons:
+ // 1. Network disconnection due to wifi off.
+ // 2. Epdg server does not respond.
+ // 3. Socket send/receive fails.
+ // Ignore this during tunnel bring down.
+ if (iwlanError.getErrorType() != IwlanError.NO_ERROR
+ && iwlanError.getErrorType()
+ != IwlanError.IKE_INTERNAL_IO_EXCEPTION) {
+ Log.e(TAG, "Unexpected error during tunnel bring down: " + iwlanError);
+ }
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ tunnelState.getDataServiceCallback(),
+ null);
+
+ return;
+ }
+
+ // just update list of data calls. No way to send error up
+ iwlanDataServiceProvider.notifyDataCallListChanged(
+ iwlanDataServiceProvider.getCallList());
+
+ // Report IwlanPdnDisconnectedReason due to the disconnection is neither for
+ // SETUP_DATA_CALL nor DEACTIVATE_DATA_CALL request.
+ metricsAtom.setDataCallFailCause(
+ ErrorPolicyManager.getInstance(
+ mContext, iwlanDataServiceProvider.getSlotIndex())
+ .getDataFailCause(apnName));
+
+ WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+ if (wifiManager == null) {
+ Log.e(TAG, "Could not find wifiManager");
+ return;
+ }
+
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ if (wifiInfo == null) {
+ Log.e(TAG, "wifiInfo is null");
+ return;
+ }
+
+ metricsAtom.setWifiSignalValue(wifiInfo.getRssi());
+ metricsAtom.setMessageId(IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED);
+ break;
+
+ case IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ iwlanDataServiceProvider.mCarrierConfigReady = true;
+ iwlanDataServiceProvider.dnsPrefetchCheck();
+ break;
+
+ case IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ iwlanDataServiceProvider.mCarrierConfigReady = false;
+ break;
+
+ case IwlanEventListener.WIFI_CALLING_ENABLE_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ iwlanDataServiceProvider.mWfcEnabled = true;
+ iwlanDataServiceProvider.dnsPrefetchCheck();
+ break;
+
+ case IwlanEventListener.WIFI_CALLING_DISABLE_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ 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 =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ if (cellInfolist != null
+ && iwlanDataServiceProvider.isRegisteredCellInfoChanged(cellInfolist)) {
+ int[] addrResolutionMethods =
+ IwlanHelper.getConfig(
+ CarrierConfigManager.Iwlan
+ .KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex());
+ for (int addrResolutionMethod : addrResolutionMethods) {
+ if (addrResolutionMethod
+ == CarrierConfigManager.Iwlan.EPDG_ADDRESS_CELLULAR_LOC) {
+ iwlanDataServiceProvider.dnsPrefetchCheck();
+ }
+ }
+ }
+ break;
+
+ case IwlanEventListener.CALL_STATE_CHANGED_EVENT:
+ iwlanDataServiceProvider =
+ (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+
+ iwlanDataServiceProvider.mCallState = msg.arg2;
+ break;
+
+ case EVENT_SETUP_DATA_CALL:
+ SetupDataCallData setupDataCallData = (SetupDataCallData) msg.obj;
+ int accessNetworkType = setupDataCallData.mAccessNetworkType;
+ @NonNull DataProfile dataProfile = setupDataCallData.mDataProfile;
+ boolean isRoaming = setupDataCallData.mIsRoaming;
+ reason = setupDataCallData.mReason;
+ LinkProperties linkProperties = setupDataCallData.mLinkProperties;
+ @IntRange(from = 0, to = 15)
+ int pduSessionId = setupDataCallData.mPduSessionId;
+ callback = setupDataCallData.mCallback;
+ iwlanDataServiceProvider = setupDataCallData.mIwlanDataServiceProvider;
+
+ if ((accessNetworkType != AccessNetworkType.IWLAN)
+ || (dataProfile == null)
+ || (linkProperties == null
+ && reason == DataService.REQUEST_REASON_HANDOVER)) {
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_ERROR_INVALID_ARG,
+ callback,
+ null);
+ return;
+ }
+
+ slotId = iwlanDataServiceProvider.getSlotIndex();
+ boolean isCSTEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, slotId);
+ boolean networkConnected =
+ isNetworkConnected(isActiveDataOnOtherSub(slotId), isCSTEnabled);
+ Log.d(
+ TAG + "[" + slotId + "]",
+ "isDds: "
+ + IwlanHelper.isDefaultDataSlot(mContext, slotId)
+ + ", isActiveDataOnOtherSub: "
+ + isActiveDataOnOtherSub(slotId)
+ + ", isCstEnabled: "
+ + isCSTEnabled
+ + ", transport: "
+ + sDefaultDataTransport);
+
+ if (!networkConnected) {
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ 5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE
+ */,
+ callback,
+ null);
+ return;
+ }
+
+ tunnelState =
+ iwlanDataServiceProvider.mTunnelStateForApn.get(
+ dataProfile.getApnSetting().getApnName());
+
+ // Return the existing PDN if the pduSessionId is the same and the tunnel
+ // state is
+ // TUNNEL_UP.
+ if (tunnelState != null) {
+ if (tunnelState.getPduSessionId() == pduSessionId
+ && tunnelState.getState()
+ == IwlanDataServiceProvider.TunnelState.TUNNEL_UP) {
+ Log.w(
+ TAG + "[" + slotId + "]",
+ "The tunnel for "
+ + dataProfile.getApnSetting().getApnName()
+ + " already exists.");
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ callback,
+ iwlanDataServiceProvider.apnTunnelStateToDataCallResponse(
+ dataProfile.getApnSetting().getApnName()));
+ } else {
+ Log.e(
+ TAG + "[" + slotId + "]",
+ "Force close the existing PDN. pduSessionId = "
+ + tunnelState.getPduSessionId()
+ + " Tunnel State = "
+ + tunnelState.getState());
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .closeTunnel(
+ dataProfile.getApnSetting().getApnName(),
+ true /* forceClose */,
+ iwlanDataServiceProvider.getIwlanTunnelCallback(),
+ iwlanDataServiceProvider.getIwlanTunnelMetrics());
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ 5 /* DataServiceCallback
+ .RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
+ callback,
+ null);
+ }
+ return;
+ }
+
+ TunnelSetupRequest.Builder tunnelReqBuilder =
+ TunnelSetupRequest.builder()
+ .setApnName(dataProfile.getApnSetting().getApnName())
+ .setIsRoaming(isRoaming)
+ .setPduSessionId(pduSessionId)
+ .setApnIpProtocol(
+ isRoaming
+ ? dataProfile
+ .getApnSetting()
+ .getRoamingProtocol()
+ : dataProfile.getApnSetting().getProtocol());
+
+ if (reason == DataService.REQUEST_REASON_HANDOVER) {
+ // for now assume that, at max, only one address of eachtype (v4/v6).
+ // TODO: Check if multiple ips can be sent in ike tunnel setup
+ for (LinkAddress lAddr : linkProperties.getLinkAddresses()) {
+ if (lAddr.isIpv4()) {
+ tunnelReqBuilder.setSrcIpv4Address(lAddr.getAddress());
+ } else if (lAddr.isIpv6()) {
+ tunnelReqBuilder.setSrcIpv6Address(lAddr.getAddress());
+ tunnelReqBuilder.setSrcIpv6AddressPrefixLength(
+ lAddr.getPrefixLength());
+ }
+ }
+ }
+
+ int apnTypeBitmask = dataProfile.getApnSetting().getApnTypeBitmask();
+ boolean isIMS = hasApnTypes(apnTypeBitmask, ApnSetting.TYPE_IMS);
+ boolean isEmergency = hasApnTypes(apnTypeBitmask, ApnSetting.TYPE_EMERGENCY);
+ tunnelReqBuilder.setRequestPcscf(isIMS || isEmergency);
+ tunnelReqBuilder.setIsEmergency(isEmergency);
+
+ iwlanDataServiceProvider.setTunnelState(
+ dataProfile,
+ callback,
+ IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP,
+ null,
+ (reason == DataService.REQUEST_REASON_HANDOVER),
+ pduSessionId,
+ isIMS || isEmergency);
+
+ boolean result =
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .bringUpTunnel(
+ tunnelReqBuilder.build(),
+ iwlanDataServiceProvider.getIwlanTunnelCallback(),
+ iwlanDataServiceProvider.getIwlanTunnelMetrics());
+ Log.d(TAG + "[" + slotId + "]", "bringup Tunnel with result:" + result);
+ if (!result) {
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_ERROR_INVALID_ARG,
+ callback,
+ null);
+ return;
+ }
+ break;
+
+ case EVENT_DEACTIVATE_DATA_CALL:
+ DeactivateDataCallData deactivateDataCallData =
+ (DeactivateDataCallData) msg.obj;
+ iwlanDataServiceProvider = deactivateDataCallData.mIwlanDataServiceProvider;
+ callback = deactivateDataCallData.mCallback;
+ reason = deactivateDataCallData.mReason;
+
+ int cid = deactivateDataCallData.mCid;
+ slotId = iwlanDataServiceProvider.getSlotIndex();
+ boolean isNetworkLost =
+ !isNetworkConnected(
+ isActiveDataOnOtherSub(slotId),
+ IwlanHelper.isCrossSimCallingEnabled(mContext, slotId));
+ boolean isHandOutSuccessful = (reason == REQUEST_REASON_HANDOVER);
+
+ for (String apn : iwlanDataServiceProvider.mTunnelStateForApn.keySet()) {
+ if (apn.hashCode() == cid) {
+ // No need to check state since dataconnection in framework serializes
+ // setup and deactivate calls using callId/cid.
+ iwlanDataServiceProvider
+ .mTunnelStateForApn
+ .get(apn)
+ .setState(
+ IwlanDataServiceProvider.TunnelState
+ .TUNNEL_IN_BRINGDOWN);
+ iwlanDataServiceProvider
+ .mTunnelStateForApn
+ .get(apn)
+ .setDataServiceCallback(callback);
+
+ // According to the handover procedure in 3GPP specifications (TS 23.402
+ // clause 8.6.1 for S1; TS 23.502 clause 4.11.4.1 for N1), if the PDN is
+ // handed out to another RAT, the IKE tunnel over ePDG SHOULD be
+ // released by the network. Thus, UE just released the tunnel locally.
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .closeTunnel(
+ apn,
+ isNetworkLost || isHandOutSuccessful /* forceClose */,
+ iwlanDataServiceProvider.getIwlanTunnelCallback(),
+ iwlanDataServiceProvider.getIwlanTunnelMetrics());
+ return;
+ }
+ }
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_DEACTIVATE_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_ERROR_INVALID_ARG,
+ callback,
+ null);
+ break;
+
+ case EVENT_DATA_CALL_LIST_REQUEST:
+ DataCallRequestData dataCallRequestData = (DataCallRequestData) msg.obj;
+ callback = dataCallRequestData.mCallback;
+ iwlanDataServiceProvider = dataCallRequestData.mIwlanDataServiceProvider;
+
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_GET_DATACALL_LIST_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ callback,
+ null);
+ break;
+
+ case EVENT_FORCE_CLOSE_TUNNEL:
+ for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
+ dp.forceCloseTunnels();
+ }
+ break;
+
+ case EVENT_ADD_DATA_SERVICE_PROVIDER:
+ iwlanDataServiceProvider = (IwlanDataServiceProvider) msg.obj;
+ addIwlanDataServiceProvider(iwlanDataServiceProvider);
+ break;
+
+ case EVENT_REMOVE_DATA_SERVICE_PROVIDER:
+ iwlanDataServiceProvider = (IwlanDataServiceProvider) msg.obj;
+
+ slotId = iwlanDataServiceProvider.getSlotIndex();
+ IwlanDataServiceProvider dsp = sIwlanDataServiceProviders.remove(slotId);
+ if (dsp == null) {
+ Log.w(TAG + "[" + slotId + "]", "No DataServiceProvider exists for slot!");
+ }
+
+ if (sIwlanDataServiceProviders.isEmpty()) {
+ deinitNetworkCallback();
+ }
+ break;
+
+ case EVENT_TUNNEL_OPENED_METRICS:
+ OnOpenedMetrics openedMetricsData = (OnOpenedMetrics) msg.obj;
+ iwlanDataServiceProvider = openedMetricsData.getIwlanDataServiceProvider();
+ apnName = openedMetricsData.getApnName();
+
+ // Record setup result for the Metrics
+ metricsAtom = iwlanDataServiceProvider.mMetricsAtomForApn.get(apnName);
+ tunnelState = iwlanDataServiceProvider.mTunnelStateForApn.get(apnName);
+ metricsAtom.setSetupRequestResult(DataServiceCallback.RESULT_SUCCESS);
+ metricsAtom.setIwlanError(IwlanError.NO_ERROR);
+ metricsAtom.setDataCallFailCause(DataFailCause.NONE);
+ metricsAtom.setTunnelState(tunnelState.getState());
+ metricsAtom.setHandoverFailureMode(-1);
+ metricsAtom.setRetryDurationMillis(0);
+ metricsAtom.setMessageId(IwlanStatsLog.IWLAN_SETUP_DATA_CALL_RESULT_REPORTED);
+ metricsAtom.setEpdgServerAddress(openedMetricsData.getEpdgServerAddress());
+ metricsAtom.setProcessingDurationMillis(
+ (int)
+ (System.currentTimeMillis()
+ - iwlanDataServiceProvider.mProcessingStartTime));
+ metricsAtom.setEpdgServerSelectionDurationMillis(
+ openedMetricsData.getEpdgServerSelectionDuration());
+ metricsAtom.setIkeTunnelEstablishmentDurationMillis(
+ openedMetricsData.getIkeTunnelEstablishmentDuration());
+
+ metricsAtom.sendMetricsData();
+ break;
+
+ case EVENT_TUNNEL_CLOSED_METRICS:
+ OnClosedMetrics closedMetricsData = (OnClosedMetrics) msg.obj;
+ iwlanDataServiceProvider = closedMetricsData.getIwlanDataServiceProvider();
+ apnName = closedMetricsData.getApnName();
+
+ metricsAtom = iwlanDataServiceProvider.mMetricsAtomForApn.get(apnName);
+ if (metricsAtom == null) {
+ Log.w(TAG, "EVENT_TUNNEL_CLOSED_METRICS: MetricsAtom is null!");
+ break;
+ }
+ metricsAtom.setEpdgServerAddress(closedMetricsData.getEpdgServerAddress());
+ metricsAtom.setProcessingDurationMillis(
+ iwlanDataServiceProvider.mProcessingStartTime > 0
+ ? (int)
+ (System.currentTimeMillis()
+ - iwlanDataServiceProvider.mProcessingStartTime)
+ : 0);
+ metricsAtom.setEpdgServerSelectionDurationMillis(
+ closedMetricsData.getEpdgServerSelectionDuration());
+ metricsAtom.setIkeTunnelEstablishmentDurationMillis(
+ closedMetricsData.getIkeTunnelEstablishmentDuration());
+
+ metricsAtom.sendMetricsData();
+ iwlanDataServiceProvider.mMetricsAtomForApn.remove(apnName);
+ break;
+
+ default:
+ throw new IllegalStateException("Unexpected value: " + msg.what);
+ }
+ }
+
+ IwlanDataServiceHandler(Looper looper) {
+ super(looper);
+ }
+ }
+
+ private static final class TunnelOpenedData {
+ final String mApnName;
+ final TunnelLinkProperties mTunnelLinkProperties;
+ final IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ private TunnelOpenedData(
+ String apnName,
+ TunnelLinkProperties tunnelLinkProperties,
+ IwlanDataServiceProvider dsp) {
+ mApnName = apnName;
+ mTunnelLinkProperties = tunnelLinkProperties;
+ mIwlanDataServiceProvider = dsp;
+ }
+ }
+
+ private static final class TunnelClosedData {
+ final String mApnName;
+ final IwlanError mIwlanError;
+ final IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ private TunnelClosedData(
+ String apnName, IwlanError iwlanError, IwlanDataServiceProvider dsp) {
+ mApnName = apnName;
+ mIwlanError = iwlanError;
+ mIwlanDataServiceProvider = dsp;
+ }
+ }
+
+ private static final class SetupDataCallData {
+ final int mAccessNetworkType;
+ @NonNull final DataProfile mDataProfile;
+ final boolean mIsRoaming;
+ final boolean mAllowRoaming;
+ final int mReason;
+ @Nullable final LinkProperties mLinkProperties;
+
+ @IntRange(from = 0, to = 15)
+ final int mPduSessionId;
+
+ @Nullable final NetworkSliceInfo mSliceInfo;
+ @Nullable final TrafficDescriptor mTrafficDescriptor;
+ final boolean mMatchAllRuleAllowed;
+ @NonNull final DataServiceCallback mCallback;
+ final IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ private SetupDataCallData(
+ int accessNetworkType,
+ DataProfile dataProfile,
+ boolean isRoaming,
+ boolean allowRoaming,
+ int reason,
+ LinkProperties linkProperties,
+ int pduSessionId,
+ NetworkSliceInfo sliceInfo,
+ TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed,
+ DataServiceCallback callback,
+ IwlanDataServiceProvider dsp) {
+ mAccessNetworkType = accessNetworkType;
+ mDataProfile = dataProfile;
+ mIsRoaming = isRoaming;
+ mAllowRoaming = allowRoaming;
+ mReason = reason;
+ mLinkProperties = linkProperties;
+ mPduSessionId = pduSessionId;
+ mSliceInfo = sliceInfo;
+ mTrafficDescriptor = trafficDescriptor;
+ mMatchAllRuleAllowed = matchAllRuleAllowed;
+ mCallback = callback;
+ mIwlanDataServiceProvider = dsp;
+ }
+ }
+
+ private static final class DeactivateDataCallData {
+ final int mCid;
+ final int mReason;
+ final DataServiceCallback mCallback;
+ final IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ private DeactivateDataCallData(
+ int cid, int reason, DataServiceCallback callback, IwlanDataServiceProvider dsp) {
+ mCid = cid;
+ mReason = reason;
+ mCallback = callback;
+ mIwlanDataServiceProvider = dsp;
+ }
+ }
+
+ private static final class DataCallRequestData {
+ final DataServiceCallback mCallback;
+ final IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ private DataCallRequestData(DataServiceCallback callback, IwlanDataServiceProvider dsp) {
+ mCallback = callback;
+ mIwlanDataServiceProvider = dsp;
+ }
+ }
+
+ static int getConnectedDataSub(NetworkCapabilities networkCapabilities) {
+ int connectedDataSub = INVALID_SUB_ID;
+ NetworkSpecifier specifier = networkCapabilities.getNetworkSpecifier();
+ TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ connectedDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ } else if (transportInfo instanceof VcnTransportInfo) {
+ connectedDataSub = ((VcnTransportInfo) transportInfo).getSubId();
+ }
+ return connectedDataSub;
+ }
+
+ static void setConnectedDataSub(int subId) {
+ mConnectedDataSub = subId;
}
@VisibleForTesting
- static synchronized boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) {
- if (!isDds && isCstEnabled) {
- // Only Non-DDS sub with CST enabled, can use any transport.
+ static boolean isActiveDataOnOtherSub(int slotId) {
+ int subId = IwlanHelper.getSubId(mContext, slotId);
+ return mConnectedDataSub != INVALID_SUB_ID && subId != mConnectedDataSub;
+ }
+
+ @VisibleForTesting
+ static boolean isNetworkConnected(boolean isActiveDataOnOtherSub, boolean isCstEnabled) {
+ if (isActiveDataOnOtherSub && isCstEnabled) {
+ // For cross-SIM IWLAN (Transport.MOBILE), an active data PDN must be maintained on the
+ // other subscription.
+ if (sNetworkConnected && (sDefaultDataTransport != Transport.MOBILE)) {
+ Log.e(TAG, "Internet is on other slot, but default transport is not MOBILE!");
+ }
return sNetworkConnected;
} else {
- // For all other cases, only wifi transport can be used.
+ // For all other cases, only Transport.WIFI can be used.
return ((sDefaultDataTransport == Transport.WIFI) && sNetworkConnected);
}
}
- @VisibleForTesting
/* Note: this api should have valid transport if networkConnected==true */
- // Only synchronize on IwlanDataService.class for changes being made to static variables
- // Calls to DataServiceProvider object methods (or any objects in the future) should
- // not be made within synchronized block protected by IwlanDataService.class
static void setNetworkConnected(
- boolean networkConnected, Network network, Transport transport) {
+ boolean networkConnected, @NonNull Network network, Transport transport) {
boolean hasNetworkChanged = false;
boolean hasTransportChanged = false;
boolean hasNetworkConnectedChanged = false;
- synchronized (IwlanDataService.class) {
- if (sNetworkConnected == networkConnected
- && network.equals(sNetwork)
- && sDefaultDataTransport == transport) {
- // Nothing changed
- return;
- }
-
- // safety check
- if (networkConnected && transport == Transport.UNSPECIFIED_NETWORK) {
- Log.e(TAG, "setNetworkConnected: Network connected but transport unspecified");
- return;
- }
+ if (sNetworkConnected == networkConnected
+ && network.equals(sNetwork)
+ && sDefaultDataTransport == transport) {
+ // Nothing changed
+ return;
+ }
- if (!network.equals(sNetwork)) {
- Log.e(TAG, "setNetworkConnected NW changed from: " + sNetwork + " TO: " + network);
- hasNetworkChanged = true;
- }
+ // safety check
+ if (networkConnected && transport == Transport.UNSPECIFIED_NETWORK) {
+ Log.e(TAG, "setNetworkConnected: Network connected but transport unspecified");
+ return;
+ }
- if (transport != sDefaultDataTransport) {
- Log.d(
- TAG,
- "Transport was changed from "
- + sDefaultDataTransport.name()
- + " to "
- + transport.name());
- hasTransportChanged = true;
- }
+ if (!network.equals(sNetwork)) {
+ Log.e(TAG, "System default network changed from: " + sNetwork + " TO: " + network);
+ hasNetworkChanged = true;
+ }
- if (sNetworkConnected != networkConnected) {
- Log.d(
- TAG,
- "Network connected state change from "
- + sNetworkConnected
- + " to "
- + networkConnected);
- hasNetworkConnectedChanged = true;
- }
+ if (transport != sDefaultDataTransport) {
+ Log.d(
+ TAG,
+ "Transport was changed from "
+ + sDefaultDataTransport.name()
+ + " to "
+ + transport.name());
+ hasTransportChanged = true;
+ }
- sNetworkConnected = networkConnected;
- sDefaultDataTransport = transport;
- sNetwork = network;
- if (!networkConnected) {
- // reset link protocol type
- sLinkProtocolType = LinkProtocolType.UNKNOWN;
- }
+ if (sNetworkConnected != networkConnected) {
+ Log.d(
+ TAG,
+ "Network connected state change from "
+ + sNetworkConnected
+ + " to "
+ + networkConnected);
+ hasNetworkConnectedChanged = true;
}
+ sNetworkConnected = networkConnected;
+ sDefaultDataTransport = transport;
+ sNetwork = network;
+
if (networkConnected) {
if (hasTransportChanged) {
// Perform forceClose for tunnels in bringdown.
// let framework handle explicit teardown
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+ for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
dp.forceCloseTunnelsInDeactivatingState();
}
}
@@ -1272,14 +1902,18 @@ public class IwlanDataService extends DataService {
}
// only prefetch dns and updateNetwork if Network has changed
if (hasNetworkChanged) {
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+ 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();
}
} else {
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+ for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
// once network is disconnected, even NAT KA offload fails
// But we should still let framework do an explicit teardown
// so as to not affect an ongoing handover
@@ -1289,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
*
@@ -1338,16 +1930,7 @@ public class IwlanDataService extends DataService {
* @return DataService.DataServiceProvider associated with the slot
*/
public static DataService.DataServiceProvider getDataServiceProvider(int slotId) {
- DataServiceProvider ret = null;
- if (!sIwlanDataServiceProviderList.isEmpty()) {
- for (IwlanDataServiceProvider provider : sIwlanDataServiceProviderList) {
- if (provider.getSlotIndex() == slotId) {
- ret = provider;
- break;
- }
- }
- }
- return ret;
+ return sIwlanDataServiceProviders.get(slotId);
}
public static Context getContext() {
@@ -1360,42 +1943,55 @@ public class IwlanDataService extends DataService {
Log.d(TAG, "Creating provider for " + slotIndex);
if (mNetworkMonitorCallback == null) {
- // start monitoring network
- mNetworkCallbackHandlerThread =
- new HandlerThread(IwlanNetworkService.class.getSimpleName());
- mNetworkCallbackHandlerThread.start();
- Looper looper = mNetworkCallbackHandlerThread.getLooper();
- Handler handler = new Handler(looper);
-
- // register for default network callback
+ // start monitoring network and register for default network callback
ConnectivityManager connectivityManager =
mContext.getSystemService(ConnectivityManager.class);
mNetworkMonitorCallback = new IwlanNetworkMonitorCallback();
- connectivityManager.registerDefaultNetworkCallback(mNetworkMonitorCallback, handler);
+ if (connectivityManager != null) {
+ connectivityManager.registerSystemDefaultNetworkCallback(
+ mNetworkMonitorCallback, getIwlanDataServiceHandler());
+ }
Log.d(TAG, "Registered with Connectivity Service");
}
IwlanDataServiceProvider dp = new IwlanDataServiceProvider(slotIndex, this);
- addIwlanDataServiceProvider(dp);
+
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(EVENT_ADD_DATA_SERVICE_PROVIDER, dp));
return dp;
}
public void removeDataServiceProvider(IwlanDataServiceProvider dp) {
- sIwlanDataServiceProviderList.remove(dp);
- if (sIwlanDataServiceProviderList.isEmpty()) {
- // deinit network related stuff
- ConnectivityManager connectivityManager =
- mContext.getSystemService(ConnectivityManager.class);
- connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback);
- mNetworkCallbackHandlerThread.quit(); // no need to quitSafely
- mNetworkCallbackHandlerThread = null;
- mNetworkMonitorCallback = null;
- }
+ getIwlanDataServiceHandler()
+ .sendMessage(
+ getIwlanDataServiceHandler()
+ .obtainMessage(EVENT_REMOVE_DATA_SERVICE_PROVIDER, dp));
}
@VisibleForTesting
void addIwlanDataServiceProvider(IwlanDataServiceProvider dp) {
- sIwlanDataServiceProviderList.add(dp);
+ int slotIndex = dp.getSlotIndex();
+ if (sIwlanDataServiceProviders.containsKey(slotIndex)) {
+ throw new IllegalStateException(
+ "DataServiceProvider already exists for slot " + slotIndex);
+ }
+ sIwlanDataServiceProviders.put(slotIndex, dp);
+ }
+
+ void deinitNetworkCallback() {
+ // deinit network related stuff
+ ConnectivityManager connectivityManager =
+ mContext.getSystemService(ConnectivityManager.class);
+ if (connectivityManager != null) {
+ connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback);
+ }
+ mNetworkMonitorCallback = null;
+ }
+
+ boolean hasApnTypes(int apnTypeBitmask, int expectedApn) {
+ return (apnTypeBitmask & expectedApn) != 0;
}
@VisibleForTesting
@@ -1408,9 +2004,67 @@ public class IwlanDataService extends DataService {
return mNetworkMonitorCallback;
}
+ @VisibleForTesting
+ @NonNull
+ Handler getIwlanDataServiceHandler() {
+ if (mIwlanDataServiceHandler == null) {
+ mIwlanDataServiceHandler = new IwlanDataServiceHandler(getLooper());
+ }
+ return mIwlanDataServiceHandler;
+ }
+
+ @VisibleForTesting
+ Looper getLooper() {
+ mIwlanDataServiceHandlerThread = new HandlerThread("IwlanDataServiceThread");
+ mIwlanDataServiceHandlerThread.start();
+ return mIwlanDataServiceHandlerThread.getLooper();
+ }
+
+ private static String eventToString(int event) {
+ switch (event) {
+ case EVENT_TUNNEL_OPENED:
+ return "EVENT_TUNNEL_OPENED";
+ case EVENT_TUNNEL_CLOSED:
+ return "EVENT_TUNNEL_CLOSED";
+ case EVENT_SETUP_DATA_CALL:
+ return "EVENT_SETUP_DATA_CALL";
+ case EVENT_DEACTIVATE_DATA_CALL:
+ return "EVENT_DEACTIVATE_DATA_CALL";
+ case EVENT_DATA_CALL_LIST_REQUEST:
+ return "EVENT_DATA_CALL_LIST_REQUEST";
+ case EVENT_FORCE_CLOSE_TUNNEL:
+ return "EVENT_FORCE_CLOSE_TUNNEL";
+ case EVENT_ADD_DATA_SERVICE_PROVIDER:
+ return "EVENT_ADD_DATA_SERVICE_PROVIDER";
+ case EVENT_REMOVE_DATA_SERVICE_PROVIDER:
+ return "EVENT_REMOVE_DATA_SERVICE_PROVIDER";
+ case IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT:
+ return "CARRIER_CONFIG_CHANGED_EVENT";
+ case IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT:
+ return "CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT";
+ case IwlanEventListener.WIFI_CALLING_ENABLE_EVENT:
+ 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:
+ return "EVENT_TUNNEL_OPENED_METRICS";
+ case EVENT_TUNNEL_CLOSED_METRICS:
+ return "EVENT_TUNNEL_CLOSED_METRICS";
+ case IwlanEventListener.CALL_STATE_CHANGED_EVENT:
+ return "CALL_STATE_CHANGED_EVENT";
+ default:
+ return "Unknown(" + event + ")";
+ }
+ }
+
@Override
public void onCreate() {
- setAppContext(getApplicationContext());
+ Context context = getApplicationContext().createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+ setAppContext(context);
IwlanBroadcastReceiver.startListening(mContext);
IwlanHelper.startCountryDetector(mContext);
}
@@ -1422,18 +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");
- // force close all the tunnels when there are no clients
- // active
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
- dp.forceCloseTunnels();
- }
+ Log.d(TAG, "IwlanDataService onUnbind");
+ getIwlanDataServiceHandler()
+ .sendMessage(getIwlanDataServiceHandler().obtainMessage(EVENT_FORCE_CLOSE_TUNNEL));
return super.onUnbind(intent);
}
@@ -1446,7 +2097,7 @@ public class IwlanDataService extends DataService {
transport = "WIFI";
}
pw.println("Default transport: " + transport);
- for (IwlanDataServiceProvider provider : sIwlanDataServiceProviderList) {
+ for (IwlanDataServiceProvider provider : sIwlanDataServiceProviders.values()) {
pw.println();
provider.dump(fd, pw, args);
pw.println();
diff --git a/src/com/google/android/iwlan/IwlanError.java b/src/com/google/android/iwlan/IwlanError.java
index f621a7a..fc03a01 100644
--- a/src/com/google/android/iwlan/IwlanError.java
+++ b/src/com/google/android/iwlan/IwlanError.java
@@ -16,16 +16,15 @@
package com.google.android.iwlan;
-import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeIOException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
+import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import java.io.IOException;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
public class IwlanError {
@@ -41,10 +40,14 @@ public class IwlanError {
public static final int EPDG_SELECTOR_SERVER_SELECTION_FAILED = 4;
public static final int TUNNEL_TRANSFORM_FAILED = 5;
public static final int SIM_NOT_READY_EXCEPTION = 6;
- public static final int NETWORK_FAILURE = 7;
-
- // Catch all exception
- public static final int UNKNOWN_EXCEPTION = 8; // catch all
+ public static final int IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 7;
+ public static final int IKE_NETWORK_LOST_EXCEPTION = 8;
+ public static final int TUNNEL_NOT_FOUND = 9;
+ public static final int EPDG_ADDRESS_ONLY_IPV4_ALLOWED = 10;
+ public static final int EPDG_ADDRESS_ONLY_IPV6_ALLOWED = 11;
+ public static final int IKE_INIT_TIMEOUT = 12;
+ public static final int IKE_MOBILITY_TIMEOUT = 13;
+ public static final int IKE_DPD_TIMEOUT = 14;
@IntDef({
NO_ERROR,
@@ -54,28 +57,38 @@ public class IwlanError {
EPDG_SELECTOR_SERVER_SELECTION_FAILED,
TUNNEL_TRANSFORM_FAILED,
SIM_NOT_READY_EXCEPTION,
- NETWORK_FAILURE,
- UNKNOWN_EXCEPTION
+ IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+ IKE_NETWORK_LOST_EXCEPTION,
+ TUNNEL_NOT_FOUND,
+ EPDG_ADDRESS_ONLY_IPV4_ALLOWED,
+ EPDG_ADDRESS_ONLY_IPV6_ALLOWED,
+ IKE_INIT_TIMEOUT,
+ IKE_MOBILITY_TIMEOUT,
+ IKE_DPD_TIMEOUT
})
- @interface IwlanErrorType {};
+ @interface IwlanErrorType {}
private static final Map<Integer, String> sErrorTypeStrings =
- new ConcurrentHashMap<>() {
- {
- put(NO_ERROR, "IWLAN_NO_ERROR");
- put(IKE_PROTOCOL_EXCEPTION, "IWLAN_IKE_PROTOCOL_EXCEPTION");
- put(IKE_INTERNAL_IO_EXCEPTION, "IWLAN_IKE_INTERNAL_IO_EXCEPTION");
- put(IKE_GENERIC_EXCEPTION, "IWLAN_IKE_GENERIC_EXCEPTION");
- put(
+ Map.ofEntries(
+ Map.entry(NO_ERROR, "IWLAN_NO_ERROR"),
+ Map.entry(IKE_PROTOCOL_EXCEPTION, "IWLAN_IKE_PROTOCOL_EXCEPTION"),
+ Map.entry(IKE_INTERNAL_IO_EXCEPTION, "IWLAN_IKE_INTERNAL_IO_EXCEPTION"),
+ Map.entry(IKE_GENERIC_EXCEPTION, "IWLAN_IKE_GENERIC_EXCEPTION"),
+ Map.entry(
EPDG_SELECTOR_SERVER_SELECTION_FAILED,
- "IWLAN_EPDG_SELECTOR_SERVER_SELECTION_FAILED");
- put(TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
- put(SIM_NOT_READY_EXCEPTION, "IWLAN_SIM_NOT_READY_EXCEPTION");
- put(NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE");
- put(UNKNOWN_EXCEPTION, "IWLAN_UNKNOWN_EXCEPTION");
- }
- };
-
+ "IWLAN_EPDG_SELECTOR_SERVER_SELECTION_FAILED"),
+ Map.entry(TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED"),
+ Map.entry(SIM_NOT_READY_EXCEPTION, "IWLAN_SIM_NOT_READY_EXCEPTION"),
+ Map.entry(
+ IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+ "IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED"),
+ Map.entry(IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION"),
+ Map.entry(TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND"),
+ Map.entry(IKE_INIT_TIMEOUT, "IKE_INIT_TIMEOUT"),
+ Map.entry(IKE_MOBILITY_TIMEOUT, "IKE_MOBILITY_TIMEOUT"),
+ Map.entry(IKE_DPD_TIMEOUT, "IKE_DPD_TIMEOUT"),
+ Map.entry(EPDG_ADDRESS_ONLY_IPV4_ALLOWED, "EPDG_ADDRESS_ONLY_IPV4_ALLOWED"),
+ Map.entry(EPDG_ADDRESS_ONLY_IPV6_ALLOWED, "EPDG_ADDRESS_ONLY_IPV6_ALLOWED"));
private int mErrorType;
private Exception mException;
@@ -83,11 +96,16 @@ public class IwlanError {
mErrorType = err;
}
+ public IwlanError(@IwlanErrorType int err, @NonNull Exception exception) {
+ mErrorType = err;
+ mException = exception;
+ }
+
/**
* Sets the IwlanError based on the Exception: 1. IkeException is base the class for all IKE
* exception ErrorType: IKE_GENERIC_EXCEPTION. 2. IkeProtocolException is for specific protocol
* errors (like IKE notify error codes) ErrorType: IKE_PROTOCOL_EXCEPTION 3.
- * IkeInternalException is just a wrapper for various exeptions that IKE lib may encounter
+ * IkeInternalException is just a wrapper for various exceptions that IKE lib may encounter
* ErrorType: IKE_INTERNAL_IO_EXCEPTION if the Exception is instance of IOException ErrorType:
* IKE_GENERIC_EXCEPTION for all the other.
*/
@@ -99,11 +117,10 @@ public class IwlanError {
IwlanErrorIkeIOException((IkeIOException) exception);
} else if (exception instanceof IkeInternalException) {
IwlanErrorIkeInternalException((IkeInternalException) exception);
- } else if (exception instanceof IkeException) {
- mErrorType = IKE_GENERIC_EXCEPTION;
- mException = exception;
+ } else if (exception instanceof IkeNetworkLostException) {
+ IwlanErrorIkeNetworkLostException((IkeNetworkLostException) exception);
} else {
- mErrorType = UNKNOWN_EXCEPTION;
+ mErrorType = IKE_GENERIC_EXCEPTION;
mException = exception;
}
}
@@ -127,6 +144,11 @@ public class IwlanError {
mException = exception;
}
+ private void IwlanErrorIkeNetworkLostException(@NonNull IkeNetworkLostException exception) {
+ mErrorType = IKE_NETWORK_LOST_EXCEPTION;
+ mException = exception;
+ }
+
public @IwlanErrorType int getErrorType() {
return mErrorType;
}
@@ -154,60 +176,31 @@ public class IwlanError {
switch (mErrorType) {
case IKE_GENERIC_EXCEPTION:
- sb.append("MSG: " + mException.getMessage() + "\n CAUSE: ");
+ sb.append("MSG: ").append(mException.getMessage()).append("\n CAUSE: ");
sb.append(mException.getCause());
break;
- case UNKNOWN_EXCEPTION:
- sb.append(mException.toString());
- break;
case IKE_PROTOCOL_EXCEPTION:
- sb.append("ERR: " + ((IkeProtocolException) mException).getErrorType() + "\nDATA:");
+ sb.append("ERR: ")
+ .append(((IkeProtocolException) mException).getErrorType())
+ .append("\nDATA:");
for (byte b : ((IkeProtocolException) mException).getErrorData()) {
sb.append(String.format("%02x ", b));
}
break;
+ case IKE_NETWORK_LOST_EXCEPTION:
+ sb.append("ERR: ")
+ .append(mException.getMessage())
+ .append("\n CAUSE: ")
+ .append(mException.getCause())
+ .append("\n NETWORK: ")
+ .append(((IkeNetworkLostException) mException).getNetwork());
+ break;
default:
sb.append("-No Details-");
}
return sb.toString();
}
- /**
- * Returns the error of the String. String that matches the name of the Error
- *
- * @param errorType string form of errorType
- * @return IwlanErrorType
- */
- public static int getErrorType(String errorType) {
- int ret = IwlanError.UNKNOWN_EXCEPTION;
-
- // TODO: Add representation for Global error
- switch (errorType) {
- case "IKE_PROTOCOL_EXCEPTION":
- ret = IwlanError.IKE_PROTOCOL_EXCEPTION;
- break;
- case "IKE_INTERNAL_IO_EXCEPTION":
- ret = IwlanError.IKE_INTERNAL_IO_EXCEPTION;
- break;
- case "IKE_GENERIC_EXCEPTION":
- ret = IwlanError.IKE_GENERIC_EXCEPTION;
- break;
- case "EPDG_SELECTOR_SERVER_SELECTION_FAILED":
- ret = IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED;
- break;
- case "TUNNEL_TRANSFORM_FAILED":
- ret = IwlanError.TUNNEL_TRANSFORM_FAILED;
- break;
- case "SIM_NOT_READY_EXCEPTION":
- ret = IwlanError.SIM_NOT_READY_EXCEPTION;
- break;
- case "NETWORK_FAILURE":
- ret = IwlanError.NETWORK_FAILURE;
- break;
- }
- return ret;
- }
-
@Override
public boolean equals(Object o) {
if (!(o instanceof IwlanError)) {
@@ -231,4 +224,3 @@ public class IwlanError {
return ret;
}
}
-;
diff --git a/src/com/google/android/iwlan/IwlanEventListener.java b/src/com/google/android/iwlan/IwlanEventListener.java
index b4d38e2..f76c179 100644
--- a/src/com/google/android/iwlan/IwlanEventListener.java
+++ b/src/com/google/android/iwlan/IwlanEventListener.java
@@ -43,6 +43,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -58,7 +59,7 @@ public class IwlanEventListener {
/** Airplane mode turned off or disabled. */
public static final int APM_DISABLE_EVENT = 3;
- /** Airplame mode turned on or enabled */
+ /** Airplane mode turned on or enabled */
public static final int APM_ENABLE_EVENT = 4;
/** Wifi AccessPoint changed. */
@@ -85,6 +86,15 @@ public class IwlanEventListener {
/** On Cellinfo changed */
public static final int CELLINFO_CHANGED_EVENT = 11;
+ /** On Call state changed */
+ public static final int CALL_STATE_CHANGED_EVENT = 12;
+
+ /* Events used and handled by IwlanDataService internally */
+ public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100;
+
+ /* Events used and handled by IwlanNetworkService internally */
+ public static final int NETWORK_SERVICE_INTERNAL_EVENT_BASE = 200;
+
@IntDef({
CARRIER_CONFIG_CHANGED_EVENT,
WIFI_DISABLE_EVENT,
@@ -96,27 +106,27 @@ public class IwlanEventListener {
CROSS_SIM_CALLING_ENABLE_EVENT,
CROSS_SIM_CALLING_DISABLE_EVENT,
CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT,
- CELLINFO_CHANGED_EVENT
+ CELLINFO_CHANGED_EVENT,
+ CALL_STATE_CHANGED_EVENT
})
- @interface IwlanEventType {};
+ @interface IwlanEventType {}
- private static String LOG_TAG = IwlanEventListener.class.getSimpleName();
+ private static final String LOG_TAG = IwlanEventListener.class.getSimpleName();
private final String SUB_TAG;
private static Boolean sIsAirplaneModeOn;
- private static String sWifiSSID = new String();
+ private static String sWifiSSID = "";
- private static Map<Integer, IwlanEventListener> mInstances = new ConcurrentHashMap<>();
+ private static final Map<Integer, IwlanEventListener> mInstances = new ConcurrentHashMap<>();
- private Context mContext;
- private int mSlotId;
+ private final Context mContext;
+ private final int mSlotId;
private int mSubId;
private Uri mCrossSimCallingUri;
private Uri mWfcEnabledUri;
private UserSettingContentObserver mUserSettingContentObserver;
- private HandlerThread mUserSettingHandlerThread;
private RadioInfoTelephonyCallback mTelephonyCallback;
SparseArray<Set<Handler>> eventHandlers = new SparseArray<>();
@@ -128,6 +138,8 @@ public class IwlanEventListener {
@Override
public void onChange(boolean selfChange, Uri uri) {
+ Objects.requireNonNull(mCrossSimCallingUri, "CrossSimCallingUri must not be null");
+ Objects.requireNonNull(mWfcEnabledUri, "WfcEnabledUri must not be null");
if (mCrossSimCallingUri.equals(uri)) {
notifyCurrentSetting(uri);
} else if (mWfcEnabledUri.equals(uri)) {
@@ -137,26 +149,46 @@ public class IwlanEventListener {
}
private class RadioInfoTelephonyCallback extends TelephonyCallback
- implements TelephonyCallback.CellInfoListener {
+ implements TelephonyCallback.CellInfoListener, TelephonyCallback.CallStateListener {
@Override
public void onCellInfoChanged(List<CellInfo> arrayCi) {
Log.d(LOG_TAG, "Cellinfo changed");
- int event = CELLINFO_CHANGED_EVENT;
for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
IwlanEventListener instance = entry.getValue();
if (instance != null) {
- instance.updateHandlers(event, arrayCi);
+ instance.updateHandlers(arrayCi);
+ }
+ }
+ }
+
+ @Override
+ public void onCallStateChanged(int state) {
+ Log.d(
+ LOG_TAG,
+ "Call state changed to " + callStateToString(state) + " for slot " + mSlotId);
+
+ for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
+ IwlanEventListener instance = entry.getValue();
+ if (instance != null) {
+ instance.updateHandlers(CALL_STATE_CHANGED_EVENT, state);
}
}
}
}
- /** Returns IwlanEventListener instance */
+ /**
+ * Returns IwlanEventListener instance
+ */
public static IwlanEventListener getInstance(@NonNull Context context, int slotId) {
return mInstances.computeIfAbsent(slotId, k -> new IwlanEventListener(context, slotId));
}
+ @VisibleForTesting
+ public static void resetAllInstances() {
+ mInstances.clear();
+ }
+
/**
* Adds handler for the list of events.
*
@@ -219,7 +251,7 @@ public class IwlanEventListener {
* Report a Broadcast received. Mainly used by IwlanBroadcastReceiver to report the following
* broadcasts CARRIER_CONFIG_CHANGED
*
- * @param Intent intent
+ * @param intent intent
*/
public static synchronized void onBroadcastReceived(Intent intent) {
int event = UNKNOWN_EVENT;
@@ -235,11 +267,11 @@ public class IwlanEventListener {
TelephonyManager.UNKNOWN_CARRIER_ID);
Context context = IwlanDataService.getContext();
if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && context != null) {
- getInstance(context, slotId).onCarrierConfigChanged(slotId, carrierId);
+ getInstance(context, slotId).onCarrierConfigChanged(carrierId);
}
break;
case Intent.ACTION_AIRPLANE_MODE_CHANGED:
- Boolean isAirplaneModeOn = new Boolean(intent.getBooleanExtra("state", false));
+ Boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
if (sIsAirplaneModeOn != null && sIsAirplaneModeOn.equals(isAirplaneModeOn)) {
// no change in apm state
break;
@@ -269,7 +301,7 @@ public class IwlanEventListener {
/**
* Broadcast WIFI_AP_CHANGED_EVENT if Wifi SSID changed after Wifi connected.
*
- * @param Context context
+ * @param context context
*/
public static void onWifiConnected(Context context) {
WifiManager wifiManager = context.getSystemService(WifiManager.class);
@@ -292,11 +324,10 @@ public class IwlanEventListener {
// Wifi.
if (sWifiSSID.length() > 0 && !sWifiSSID.equals(wifiSSID)) {
Log.d(LOG_TAG, "Wifi SSID changed");
- int event = WIFI_AP_CHANGED_EVENT;
for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
IwlanEventListener instance = entry.getValue();
if (instance != null) {
- instance.updateHandlers(event);
+ instance.updateHandlers(WIFI_AP_CHANGED_EVENT);
}
}
}
@@ -307,7 +338,6 @@ public class IwlanEventListener {
* Returns the Event id of the String. String that matches the name of the event
*
* @param event String form of the event.
- * @param int form of the event.
*/
public static int getUnthrottlingEvent(String event) {
int ret = UNKNOWN_EVENT;
@@ -357,7 +387,7 @@ public class IwlanEventListener {
sIsAirplaneModeOn = null;
}
- private void onCarrierConfigChanged(int slotId, int carrierId) {
+ private void onCarrierConfigChanged(int carrierId) {
Log.d(SUB_TAG, "onCarrierConfigChanged");
int subId = IwlanHelper.getSubId(mContext, mSlotId);
if (subId != mSubId) {
@@ -379,7 +409,7 @@ public class IwlanEventListener {
}
/** Unregister ContentObserver. */
- private void unregisterContentObserver() {
+ void unregisterContentObserver() {
if (mUserSettingContentObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mUserSettingContentObserver);
}
@@ -390,10 +420,10 @@ public class IwlanEventListener {
/** Initiate ContentObserver if it is not created. And, register it with the current sub id. */
private void registerContentObserver() {
if (mUserSettingContentObserver == null) {
- mUserSettingHandlerThread =
+ HandlerThread userSettingHandlerThread =
new HandlerThread(IwlanNetworkService.class.getSimpleName());
- mUserSettingHandlerThread.start();
- Looper looper = mUserSettingHandlerThread.getLooper();
+ userSettingHandlerThread.start();
+ Looper looper = userSettingHandlerThread.getLooper();
Handler handler = new Handler(looper);
mUserSettingContentObserver = new UserSettingContentObserver(handler);
}
@@ -448,7 +478,12 @@ public class IwlanEventListener {
Log.e(SUB_TAG, "Could not find ImsMmTelManager");
return;
}
- boolean wfcEnabled = imsMmTelManager.isVoWiFiSettingEnabled();
+ boolean wfcEnabled = false;
+ try {
+ wfcEnabled = imsMmTelManager.isVoWiFiSettingEnabled();
+ } catch (IllegalArgumentException e) {
+ Log.w(SUB_TAG, e.getMessage());
+ }
int event = (wfcEnabled) ? WIFI_CALLING_ENABLE_EVENT : WIFI_CALLING_DISABLE_EVENT;
getInstance(mContext, slotIndex).updateHandlers(event);
} else {
@@ -461,9 +496,10 @@ public class IwlanEventListener {
Log.d(SUB_TAG, "registerTelephonyCallback");
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
telephonyManager =
- telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ Objects.requireNonNull(telephonyManager)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
mTelephonyCallback = new RadioInfoTelephonyCallback();
- telephonyManager.registerTelephonyCallback(r -> r.run(), mTelephonyCallback);
+ telephonyManager.registerTelephonyCallback(Runnable::run, mTelephonyCallback);
}
@VisibleForTesting
@@ -485,17 +521,40 @@ public class IwlanEventListener {
if (eventHandlers.contains(event)) {
Log.d(SUB_TAG, "Updating handlers for the event: " + event);
for (Handler handler : eventHandlers.get(event)) {
- handler.obtainMessage(event).sendToTarget();
+ handler.obtainMessage(event, mSlotId, 0 /* unused */).sendToTarget();
+ }
+ }
+ }
+
+ private synchronized void updateHandlers(List<CellInfo> arrayCi) {
+ int event = IwlanEventListener.CELLINFO_CHANGED_EVENT;
+ if (eventHandlers.contains(event)) {
+ Log.d(SUB_TAG, "Updating handlers for the event: " + event);
+ for (Handler handler : eventHandlers.get(event)) {
+ handler.obtainMessage(event, mSlotId, 0 /* unused */, arrayCi).sendToTarget();
}
}
}
- private synchronized void updateHandlers(int event, List<CellInfo> arrayCi) {
+ private synchronized void updateHandlers(int event, int state) {
if (eventHandlers.contains(event)) {
Log.d(SUB_TAG, "Updating handlers for the event: " + event);
for (Handler handler : eventHandlers.get(event)) {
- handler.obtainMessage(event, arrayCi).sendToTarget();
+ handler.obtainMessage(event, mSlotId, state).sendToTarget();
}
}
}
+
+ private String callStateToString(int state) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ return "CALL_STATE_IDLE";
+ case TelephonyManager.CALL_STATE_RINGING:
+ return "CALL_STATE_RINGING";
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ return "CALL_STATE_OFFHOOK";
+ default:
+ return "Unknown Call State (" + state + ")";
+ }
+ }
}
diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java
index 7e39059..ade7189 100644
--- a/src/com/google/android/iwlan/IwlanHelper.java
+++ b/src/com/google/android/iwlan/IwlanHelper.java
@@ -27,6 +27,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -109,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()) {
@@ -131,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).
*
@@ -161,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;
}
@@ -313,4 +298,9 @@ public class IwlanHelper {
}
}
}
+
+ static long elapsedRealtime() {
+ /*Returns milliseconds since boot, including time spent in sleep.*/
+ return SystemClock.elapsedRealtime();
+ }
}
diff --git a/src/com/google/android/iwlan/IwlanNetworkService.java b/src/com/google/android/iwlan/IwlanNetworkService.java
index 6adbaff..00f0907 100644
--- a/src/com/google/android/iwlan/IwlanNetworkService.java
+++ b/src/com/google/android/iwlan/IwlanNetworkService.java
@@ -25,11 +25,17 @@ import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.support.annotation.NonNull;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
@@ -41,28 +47,42 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
public class IwlanNetworkService extends NetworkService {
private static final String TAG = IwlanNetworkService.class.getSimpleName();
- private Context mContext;
+ private static Context mContext;
private IwlanNetworkMonitorCallback mNetworkMonitorCallback;
private IwlanOnSubscriptionsChangedListener mSubsChangeListener;
- private HandlerThread mNetworkCallbackHandlerThread;
+ private Handler mIwlanNetworkServiceHandler;
+ private HandlerThread mIwlanNetworkServiceHandlerThread;
private static boolean sNetworkConnected;
- private static List<IwlanNetworkServiceProvider> sIwlanNetworkServiceProviderList =
- new ArrayList<IwlanNetworkServiceProvider>();
+ private static final Map<Integer, IwlanNetworkServiceProvider> sIwlanNetworkServiceProviders =
+ new ConcurrentHashMap<>();
+ private static final int INVALID_SUB_ID = -1;
+
+ // The current subscription with the active internet PDN. Need not be the default data sub.
+ // If internet is over WiFi, this value will be INVALID_SUB_ID.
+ private static int mConnectedDataSub = INVALID_SUB_ID;
+
+ private static final int EVENT_BASE = IwlanEventListener.NETWORK_SERVICE_INTERNAL_EVENT_BASE;
+ private static final int EVENT_NETWORK_REGISTRATION_INFO_REQUEST = EVENT_BASE;
+ private static final int EVENT_CREATE_NETWORK_SERVICE_PROVIDER = EVENT_BASE + 1;
+ private static final int EVENT_REMOVE_NETWORK_SERVICE_PROVIDER = EVENT_BASE + 2;
@VisibleForTesting
enum Transport {
UNSPECIFIED_NETWORK,
MOBILE,
- WIFI;
+ WIFI
}
private static Transport sDefaultDataTransport = Transport.UNSPECIFIED_NETWORK;
+ // This callback runs in the same thread as IwlanNetworkServiceHandler
final class IwlanNetworkMonitorCallback extends ConnectivityManager.NetworkCallback {
/** Called when the framework connects and has declared a new network ready for use. */
@Override
@@ -72,10 +92,11 @@ public class IwlanNetworkService extends NetworkService {
/**
* Called when the network is about to be lost, typically because there are no outstanding
- * requests left for it. This may be paired with a {@link NetworkCallback#onAvailable} call
- * with the new replacement network for graceful handover. This method is not guaranteed to
- * be called before {@link NetworkCallback#onLost} is called, for example in case a network
- * is suddenly disconnected.
+ * requests left for it. This may be paired with a {@link
+ * ConnectivityManager.NetworkCallback#onAvailable} call with the new replacement network
+ * for graceful handover. This method is not guaranteed to be called before {@link
+ * ConnectivityManager.NetworkCallback#onLost} is called, for example in case a network is
+ * suddenly disconnected.
*/
@Override
public void onLosing(Network network, int maxMsToLive) {
@@ -89,6 +110,7 @@ public class IwlanNetworkService extends NetworkService {
@Override
public void onLost(Network network) {
Log.d(TAG, "onLost: " + network);
+ IwlanNetworkService.setConnectedDataSub(INVALID_SUB_ID);
IwlanNetworkService.setNetworkConnected(false, Transport.UNSPECIFIED_NETWORK);
}
@@ -113,9 +135,12 @@ public class IwlanNetworkService extends NetworkService {
Log.d(TAG, "onCapabilitiesChanged: " + network);
if (networkCapabilities != null) {
if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ IwlanNetworkService.setConnectedDataSub(
+ getConnectedDataSub(networkCapabilities));
IwlanNetworkService.setNetworkConnected(
true, IwlanNetworkService.Transport.MOBILE);
} else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+ IwlanNetworkService.setConnectedDataSub(INVALID_SUB_ID);
IwlanNetworkService.setNetworkConnected(
true, IwlanNetworkService.Transport.WIFI);
} else {
@@ -128,12 +153,12 @@ public class IwlanNetworkService extends NetworkService {
final class IwlanOnSubscriptionsChangedListener
extends SubscriptionManager.OnSubscriptionsChangedListener {
/**
- * Callback invoked when there is any change to any SubscriptionInfo. Typically this method
+ * Callback invoked when there is any change to any SubscriptionInfo. Typically, this method
* invokes {@link SubscriptionManager#getActiveSubscriptionInfoList}
*/
@Override
public void onSubscriptionsChanged() {
- for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) {
+ for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviders.values()) {
np.subscriptionChanged();
}
}
@@ -144,45 +169,6 @@ public class IwlanNetworkService extends NetworkService {
private final IwlanNetworkService mIwlanNetworkService;
private final String SUB_TAG;
private boolean mIsSubActive = false;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
-
- private final class NSPHandler extends Handler {
- private final String TAG =
- IwlanNetworkService.class.getSimpleName()
- + NSPHandler.class.getSimpleName()
- + "["
- + getSlotIndex()
- + "]";
-
- @Override
- public void handleMessage(Message msg) {
- Log.d(TAG, "msg.what = " + msg.what);
- switch (msg.what) {
- case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT:
- Log.d(TAG, "CROSS_SIM_CALLING_ENABLE_EVENT");
- notifyNetworkRegistrationInfoChanged();
- break;
- case IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT:
- Log.d(TAG, "CROSS_SIM_CALLING_DISABLE_EVENT");
- notifyNetworkRegistrationInfoChanged();
- break;
- default:
- Log.d(TAG, "Unknown message received!");
- break;
- }
- }
-
- NSPHandler(Looper looper) {
- super(looper);
- }
- }
-
- Looper getLooper() {
- mHandlerThread = new HandlerThread("NSPHandlerThread");
- mHandlerThread.start();
- return mHandlerThread.getLooper();
- }
/**
* Constructor
@@ -195,53 +181,22 @@ public class IwlanNetworkService extends NetworkService {
mIwlanNetworkService = iwlanNetworkService;
// Register IwlanEventListener
- initHandler();
List<Integer> events = new ArrayList<Integer>();
events.add(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT);
events.add(IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT);
- IwlanEventListener.getInstance(mContext, slotIndex).addEventListener(events, mHandler);
- }
-
- void initHandler() {
- mHandler = new NSPHandler(getLooper());
+ IwlanEventListener.getInstance(mContext, slotIndex)
+ .addEventListener(events, getIwlanNetworkServiceHandler());
}
@Override
public void requestNetworkRegistrationInfo(int domain, NetworkServiceCallback callback) {
- if (callback == null) {
- Log.d(SUB_TAG, "Error: callback is null. returning");
- return;
- }
- if (domain != NetworkRegistrationInfo.DOMAIN_PS) {
- callback.onRequestNetworkRegistrationInfoComplete(
- NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
- return;
- }
-
- NetworkRegistrationInfo.Builder nriBuilder = new NetworkRegistrationInfo.Builder();
- nriBuilder
- .setAvailableServices(Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .setEmergencyOnly(!mIsSubActive)
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS);
-
- if (!IwlanNetworkService.isNetworkConnected(
- IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
- IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()))) {
- nriBuilder
- .setRegistrationState(
- NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UNKNOWN);
- Log.d(SUB_TAG, "reg state REGISTRATION_STATE_NOT_REGISTERED_SEARCHING");
- } else {
- nriBuilder
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
- .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN);
- Log.d(SUB_TAG, "reg state REGISTRATION_STATE_HOME");
- }
-
- callback.onRequestNetworkRegistrationInfoComplete(
- NetworkServiceCallback.RESULT_SUCCESS, nriBuilder.build());
+ getIwlanNetworkServiceHandler()
+ .sendMessage(
+ getIwlanNetworkServiceHandler()
+ .obtainMessage(
+ EVENT_NETWORK_REGISTRATION_INFO_REQUEST,
+ new NetworkRegistrationInfoRequestData(
+ domain, callback, this)));
}
/**
@@ -252,17 +207,16 @@ public class IwlanNetworkService extends NetworkService {
@Override
public void close() {
mIwlanNetworkService.removeNetworkServiceProvider(this);
- IwlanEventListener.getInstance(mContext, getSlotIndex()).removeEventListener(mHandler);
- mHandlerThread.quit();
+ IwlanEventListener.getInstance(mContext, getSlotIndex())
+ .removeEventListener(getIwlanNetworkServiceHandler());
}
@VisibleForTesting
void subscriptionChanged() {
- boolean subActive = false;
- SubscriptionManager sm = SubscriptionManager.from(mContext);
- if (sm.getActiveSubscriptionInfoForSimSlotIndex(getSlotIndex()) != null) {
- subActive = true;
- }
+ boolean subActive =
+ getSubscriptionManager()
+ .getActiveSubscriptionInfoForSimSlotIndex(getSlotIndex())
+ != null;
if (subActive == mIsSubActive) {
return;
}
@@ -277,6 +231,122 @@ public class IwlanNetworkService extends NetworkService {
}
}
+ private final class IwlanNetworkServiceHandler extends Handler {
+ private final String TAG = IwlanNetworkServiceHandler.class.getSimpleName();
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "msg.what = " + eventToString(msg.what));
+
+ IwlanNetworkServiceProvider iwlanNetworkServiceProvider;
+ int slotId;
+
+ switch (msg.what) {
+ case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT:
+ case IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT:
+ iwlanNetworkServiceProvider = getNetworkServiceProvider(msg.arg1);
+ iwlanNetworkServiceProvider.notifyNetworkRegistrationInfoChanged();
+ break;
+
+ case EVENT_NETWORK_REGISTRATION_INFO_REQUEST:
+ NetworkRegistrationInfoRequestData networkRegistrationInfoRequestData =
+ (NetworkRegistrationInfoRequestData) msg.obj;
+ int domain = networkRegistrationInfoRequestData.mDomain;
+ NetworkServiceCallback callback = networkRegistrationInfoRequestData.mCallback;
+ iwlanNetworkServiceProvider =
+ networkRegistrationInfoRequestData.mIwlanNetworkServiceProvider;
+
+ if (callback == null) {
+ Log.d(TAG, "Error: callback is null. returning");
+ return;
+ }
+ if (domain != NetworkRegistrationInfo.DOMAIN_PS) {
+ callback.onRequestNetworkRegistrationInfoComplete(
+ NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ return;
+ }
+
+ NetworkRegistrationInfo.Builder nriBuilder =
+ new NetworkRegistrationInfo.Builder();
+ nriBuilder
+ .setAvailableServices(
+ List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setEmergencyOnly(!iwlanNetworkServiceProvider.mIsSubActive)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS);
+
+ slotId = iwlanNetworkServiceProvider.getSlotIndex();
+ if (!IwlanNetworkService.isNetworkConnected(
+ isActiveDataOnOtherSub(slotId),
+ IwlanHelper.isCrossSimCallingEnabled(mContext, slotId))) {
+ nriBuilder
+ .setRegistrationState(
+ NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_SEARCHING)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ Log.d(
+ TAG + "[" + slotId + "]",
+ ": reg state" + " REGISTRATION_STATE_NOT_REGISTERED_SEARCHING");
+ } else {
+ nriBuilder
+ .setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN);
+ Log.d(TAG + "[" + slotId + "]", ": reg state REGISTRATION_STATE_HOME");
+ }
+
+ callback.onRequestNetworkRegistrationInfoComplete(
+ NetworkServiceCallback.RESULT_SUCCESS, nriBuilder.build());
+ break;
+
+ case EVENT_CREATE_NETWORK_SERVICE_PROVIDER:
+ iwlanNetworkServiceProvider = (IwlanNetworkServiceProvider) msg.obj;
+
+ if (sIwlanNetworkServiceProviders.isEmpty()) {
+ initCallback();
+ }
+
+ addIwlanNetworkServiceProvider(iwlanNetworkServiceProvider);
+ break;
+
+ case EVENT_REMOVE_NETWORK_SERVICE_PROVIDER:
+ iwlanNetworkServiceProvider = (IwlanNetworkServiceProvider) msg.obj;
+ slotId = iwlanNetworkServiceProvider.getSlotIndex();
+ IwlanNetworkServiceProvider nsp = sIwlanNetworkServiceProviders.remove(slotId);
+ if (nsp == null) {
+ Log.w(
+ TAG + "[" + slotId + "]",
+ "No NetworkServiceProvider exists for slot!");
+ return;
+ }
+ if (sIwlanNetworkServiceProviders.isEmpty()) {
+ deinitCallback();
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("Unexpected value: " + msg.what);
+ }
+ }
+
+ IwlanNetworkServiceHandler(Looper looper) {
+ super(looper);
+ }
+ }
+
+ private static final class NetworkRegistrationInfoRequestData {
+ final int mDomain;
+ final NetworkServiceCallback mCallback;
+ final IwlanNetworkServiceProvider mIwlanNetworkServiceProvider;
+
+ private NetworkRegistrationInfoRequestData(
+ int domain, NetworkServiceCallback callback, IwlanNetworkServiceProvider nsp) {
+ mDomain = domain;
+ mCallback = callback;
+ mIwlanNetworkServiceProvider = nsp;
+ }
+ }
+
/**
* Create the instance of {@link NetworkServiceProvider}. Network service provider must override
* this method to facilitate the creation of {@link NetworkServiceProvider} instances. The
@@ -292,37 +362,43 @@ public class IwlanNetworkService extends NetworkService {
// TODO: validity check slot index
- if (sIwlanNetworkServiceProviderList.isEmpty()) {
- // first invocation
- mNetworkCallbackHandlerThread =
- new HandlerThread(IwlanNetworkService.class.getSimpleName());
- mNetworkCallbackHandlerThread.start();
- Looper looper = mNetworkCallbackHandlerThread.getLooper();
- Handler handler = new Handler(looper);
-
- // register for default network callback
- ConnectivityManager connectivityManager =
- mContext.getSystemService(ConnectivityManager.class);
- mNetworkMonitorCallback = new IwlanNetworkMonitorCallback();
- connectivityManager.registerDefaultNetworkCallback(mNetworkMonitorCallback, handler);
- Log.d(TAG, "Registered with Connectivity Service");
-
- /* register with subscription manager */
- SubscriptionManager subscriptionManager =
- mContext.getSystemService(SubscriptionManager.class);
- mSubsChangeListener = new IwlanOnSubscriptionsChangedListener();
- subscriptionManager.addOnSubscriptionsChangedListener(mSubsChangeListener);
- Log.d(TAG, "Registered with Subscription Service");
- }
-
IwlanNetworkServiceProvider np = new IwlanNetworkServiceProvider(slotIndex, this);
- sIwlanNetworkServiceProviderList.add(np);
+ getIwlanNetworkServiceHandler()
+ .sendMessage(
+ getIwlanNetworkServiceHandler()
+ .obtainMessage(EVENT_CREATE_NETWORK_SERVICE_PROVIDER, np));
return np;
}
- public static boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) {
- if (!isDds && isCstEnabled) {
- // Only Non-DDS sub with CST enabled, can use any transport.
+ static void setConnectedDataSub(int subId) {
+ mConnectedDataSub = subId;
+ }
+
+ static int getConnectedDataSub(NetworkCapabilities networkCapabilities) {
+ int connectedDataSub = INVALID_SUB_ID;
+ NetworkSpecifier specifier = networkCapabilities.getNetworkSpecifier();
+ TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ connectedDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+ } else if (transportInfo instanceof VcnTransportInfo) {
+ connectedDataSub = ((VcnTransportInfo) transportInfo).getSubId();
+ }
+ return connectedDataSub;
+ }
+
+ static boolean isActiveDataOnOtherSub(int slotId) {
+ int subId = IwlanHelper.getSubId(mContext, slotId);
+ return mConnectedDataSub != INVALID_SUB_ID && subId != mConnectedDataSub;
+ }
+
+ public static boolean isNetworkConnected(boolean isActiveDataOnOtherSub, boolean isCstEnabled) {
+ if (isActiveDataOnOtherSub && isCstEnabled) {
+ // For cross-SIM IWLAN (Transport.MOBILE), an active data PDN must be maintained on the
+ // other subscription.
+ if (sNetworkConnected && (sDefaultDataTransport != Transport.MOBILE)) {
+ Log.e(TAG, "Internet is on other slot, but default transport is not MOBILE!");
+ }
return sNetworkConnected;
} else {
// For all other cases, only wifi transport can be used.
@@ -340,32 +416,56 @@ public class IwlanNetworkService extends NetworkService {
sNetworkConnected = connected;
sDefaultDataTransport = transport;
- for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) {
+ for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviders.values()) {
np.notifyNetworkRegistrationInfoChanged();
}
}
- public void removeNetworkServiceProvider(IwlanNetworkServiceProvider np) {
- sIwlanNetworkServiceProviderList.remove(np);
- if (sIwlanNetworkServiceProviderList.isEmpty()) {
- // deinit network related stuff
- ConnectivityManager connectivityManager =
- mContext.getSystemService(ConnectivityManager.class);
- connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback);
- mNetworkCallbackHandlerThread.quit(); // no need to quitSafely
- mNetworkCallbackHandlerThread = null;
- mNetworkMonitorCallback = null;
-
- // deinit subscription manager related stuff
- SubscriptionManager subscriptionManager =
- mContext.getSystemService(SubscriptionManager.class);
- subscriptionManager.removeOnSubscriptionsChangedListener(mSubsChangeListener);
- mSubsChangeListener = null;
+ void addIwlanNetworkServiceProvider(IwlanNetworkServiceProvider np) {
+ int slotIndex = np.getSlotIndex();
+ if (sIwlanNetworkServiceProviders.containsKey(slotIndex)) {
+ throw new IllegalStateException(
+ "NetworkServiceProvider already exists for slot " + slotIndex);
}
+ sIwlanNetworkServiceProviders.put(slotIndex, np);
+ }
+
+ public void removeNetworkServiceProvider(IwlanNetworkServiceProvider np) {
+ getIwlanNetworkServiceHandler()
+ .sendMessage(
+ getIwlanNetworkServiceHandler()
+ .obtainMessage(EVENT_REMOVE_NETWORK_SERVICE_PROVIDER, np));
}
- Context getContext() {
- return getApplicationContext();
+ void initCallback() {
+ // register for default network callback
+ mNetworkMonitorCallback = new IwlanNetworkMonitorCallback();
+ getConnectivityManager()
+ .registerSystemDefaultNetworkCallback(
+ mNetworkMonitorCallback, getIwlanNetworkServiceHandler());
+ Log.d(TAG, "Registered with Connectivity Service");
+
+ /* register with subscription manager */
+ mSubsChangeListener = new IwlanOnSubscriptionsChangedListener();
+ getSubscriptionManager()
+ .addOnSubscriptionsChangedListener(
+ new HandlerExecutor(getIwlanNetworkServiceHandler()), mSubsChangeListener);
+ Log.d(TAG, "Registered with Subscription Service");
+ }
+
+ void deinitCallback() {
+ // deinit network related stuff
+ getConnectivityManager().unregisterNetworkCallback(mNetworkMonitorCallback);
+ mNetworkMonitorCallback = null;
+
+ // deinit subscription manager related stuff
+ getSubscriptionManager().removeOnSubscriptionsChangedListener(mSubsChangeListener);
+ mSubsChangeListener = null;
+ if (mIwlanNetworkServiceHandlerThread != null) {
+ mIwlanNetworkServiceHandlerThread.quit();
+ mIwlanNetworkServiceHandlerThread = null;
+ }
+ mIwlanNetworkServiceHandler = null;
}
@VisibleForTesting
@@ -375,12 +475,45 @@ public class IwlanNetworkService extends NetworkService {
@VisibleForTesting
IwlanNetworkServiceProvider getNetworkServiceProvider(int slotIndex) {
- for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) {
- if (np.getSlotIndex() == slotIndex) {
- return np;
- }
+ return sIwlanNetworkServiceProviders.get(slotIndex);
+ }
+
+ @VisibleForTesting
+ IwlanNetworkMonitorCallback getNetworkMonitorCallback() {
+ return mNetworkMonitorCallback;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ Handler getIwlanNetworkServiceHandler() {
+ if (mIwlanNetworkServiceHandler == null) {
+ mIwlanNetworkServiceHandler = new IwlanNetworkServiceHandler(getLooper());
+ }
+ return mIwlanNetworkServiceHandler;
+ }
+
+ @VisibleForTesting
+ Looper getLooper() {
+ mIwlanNetworkServiceHandlerThread = new HandlerThread("IwlanNetworkServiceThread");
+ mIwlanNetworkServiceHandlerThread.start();
+ return mIwlanNetworkServiceHandlerThread.getLooper();
+ }
+
+ private static String eventToString(int event) {
+ switch (event) {
+ case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT:
+ return "CROSS_SIM_CALLING_ENABLE_EVENT";
+ case IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT:
+ return "CROSS_SIM_CALLING_DISABLE_EVENT";
+ case EVENT_NETWORK_REGISTRATION_INFO_REQUEST:
+ return "EVENT_NETWORK_REGISTRATION_INFO_REQUEST";
+ case EVENT_CREATE_NETWORK_SERVICE_PROVIDER:
+ return "EVENT_CREATE_NETWORK_SERVICE_PROVIDER";
+ case EVENT_REMOVE_NETWORK_SERVICE_PROVIDER:
+ return "EVENT_REMOVE_NETWORK_SERVICE_PROVIDER";
+ default:
+ return "Unknown(" + event + ")";
}
- return null;
}
@Override
@@ -393,4 +526,14 @@ public class IwlanNetworkService extends NetworkService {
Log.d(TAG, "IwlanNetworkService onBind");
return super.onBind(intent);
}
+
+ @NonNull
+ ConnectivityManager getConnectivityManager() {
+ return Objects.requireNonNull(mContext.getSystemService(ConnectivityManager.class));
+ }
+
+ @NonNull
+ SubscriptionManager getSubscriptionManager() {
+ return Objects.requireNonNull(mContext.getSystemService(SubscriptionManager.class));
+ }
}
diff --git a/src/com/google/android/iwlan/IwlanTunnelMetricsImpl.java b/src/com/google/android/iwlan/IwlanTunnelMetricsImpl.java
new file mode 100644
index 0000000..3c04de5
--- /dev/null
+++ b/src/com/google/android/iwlan/IwlanTunnelMetricsImpl.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.iwlan;
+
+import android.os.Handler;
+import com.google.android.iwlan.IwlanDataService.IwlanDataServiceProvider;
+
+public class IwlanTunnelMetricsImpl implements TunnelMetricsInterface {
+ IwlanDataServiceProvider mDataServiceProvider;
+ Handler mIwlanDataServiceHandler;
+
+ private static final int EVENT_BASE = IwlanEventListener.DATA_SERVICE_INTERNAL_EVENT_BASE;
+ private static final int EVENT_TUNNEL_OPENED_METRICS = EVENT_BASE + 8;
+ private static final int EVENT_TUNNEL_CLOSED_METRICS = EVENT_BASE + 9;
+
+ public IwlanTunnelMetricsImpl(IwlanDataServiceProvider dsp, Handler handler) {
+ mDataServiceProvider = dsp;
+ mIwlanDataServiceHandler = handler;
+ }
+
+ public void onOpened(OnOpenedMetrics metricsData) {
+ metricsData.setIwlanDataServiceProvider(mDataServiceProvider);
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(EVENT_TUNNEL_OPENED_METRICS, metricsData));
+ }
+
+ public void onClosed(OnClosedMetrics metricsData) {
+ metricsData.setIwlanDataServiceProvider(mDataServiceProvider);
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(EVENT_TUNNEL_CLOSED_METRICS, metricsData));
+ }
+}
diff --git a/src/com/google/android/iwlan/TunnelMetricsInterface.java b/src/com/google/android/iwlan/TunnelMetricsInterface.java
new file mode 100644
index 0000000..6e79688
--- /dev/null
+++ b/src/com/google/android/iwlan/TunnelMetricsInterface.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.iwlan;
+
+import java.net.InetAddress;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.google.android.iwlan.IwlanDataService.IwlanDataServiceProvider;
+
+import java.util.Objects;
+
+public interface TunnelMetricsInterface {
+ /** Called for logging the tunnel is opened. */
+ void onOpened(OnOpenedMetrics metricsData);
+ /** Called for logging the tunnel is closed or bring up failed. */
+ void onClosed(OnClosedMetrics metricsData);
+
+ static class TunnelMetricsData {
+ private final String mApnName;
+ private final String mEpdgServerAddress;
+ private final int mEpdgServerSelectionDuration;
+ private final int mIkeTunnelEstablishmentDuration;
+ private IwlanDataServiceProvider mIwlanDataServiceProvider;
+
+ protected TunnelMetricsData(Builder builder) {
+ this.mApnName = builder.mApnName;
+ this.mEpdgServerAddress = builder.mEpdgServerAddress;
+ this.mEpdgServerSelectionDuration = builder.mEpdgServerSelectionDuration;
+ this.mIkeTunnelEstablishmentDuration = builder.mIkeTunnelEstablishmentDuration;
+ }
+
+ @Nullable
+ public String getApnName() {
+ return mApnName;
+ }
+
+ @Nullable
+ public String getEpdgServerAddress() {
+ return mEpdgServerAddress;
+ }
+
+ public int getEpdgServerSelectionDuration() {
+ return mEpdgServerSelectionDuration;
+ }
+
+ public int getIkeTunnelEstablishmentDuration() {
+ return mIkeTunnelEstablishmentDuration;
+ }
+
+ public IwlanDataServiceProvider getIwlanDataServiceProvider() {
+ return mIwlanDataServiceProvider;
+ }
+
+ public void setIwlanDataServiceProvider(IwlanDataServiceProvider dsp) {
+ mIwlanDataServiceProvider = dsp;
+ }
+
+ public static class Builder<T extends Builder> {
+ @Nullable private String mApnName = null;
+ @Nullable private String mEpdgServerAddress = null;
+ private int mEpdgServerSelectionDuration = 0;
+ private int mIkeTunnelEstablishmentDuration = 0;
+
+ /** Default constructor for Builder. */
+ public Builder() {}
+
+ public T setApnName(@NonNull String apnName) {
+ mApnName = Objects.requireNonNull(apnName, "apnName must not be null");
+ return (T) this;
+ }
+
+ public T setEpdgServerAddress(InetAddress epdgAddress) {
+ mEpdgServerAddress = epdgAddress == null ? null : epdgAddress.getHostAddress();
+ return (T) this;
+ }
+
+ public T setEpdgServerSelectionDuration(int epdgServerSelectionDuration) {
+ mEpdgServerSelectionDuration = epdgServerSelectionDuration;
+ return (T) this;
+ }
+
+ public T setIkeTunnelEstablishmentDuration(int ikeTunnelEstablishmentDuration) {
+ mIkeTunnelEstablishmentDuration = ikeTunnelEstablishmentDuration;
+ return (T) this;
+ }
+
+ public TunnelMetricsData build() {
+ if (mApnName == null) {
+ throw new IllegalArgumentException("Necessary parameter missing.");
+ }
+ return new TunnelMetricsData(this);
+ }
+ }
+ }
+
+ static class OnOpenedMetrics extends TunnelMetricsData {
+
+ protected OnOpenedMetrics(Builder builder) {
+ super(builder);
+ }
+
+ public static class Builder extends TunnelMetricsData.Builder<Builder> {
+
+ public Builder() {}
+
+ public OnOpenedMetrics build() {
+ return new OnOpenedMetrics(this);
+ }
+ }
+ }
+
+ static class OnClosedMetrics extends TunnelMetricsData {
+
+ protected OnClosedMetrics(Builder builder) {
+ super(builder);
+ }
+
+ public static class Builder extends TunnelMetricsData.Builder<Builder> {
+
+ public Builder() {}
+
+ public OnClosedMetrics build() {
+ return new OnClosedMetrics(this);
+ }
+ }
+ }
+}
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index 54b082f..4534b81 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -19,6 +19,7 @@ package com.google.android.iwlan.epdg;
import android.content.Context;
import android.net.DnsResolver;
import android.net.DnsResolver.DnsException;
+import android.net.InetAddresses;
import android.net.Network;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
@@ -34,6 +35,7 @@ import android.telephony.CellInfoLte;
import android.telephony.CellInfoNr;
import android.telephony.CellInfoTdscdma;
import android.telephony.CellInfoWcdma;
+import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -42,6 +44,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.google.android.iwlan.ErrorPolicyManager;
import com.google.android.iwlan.IwlanError;
import com.google.android.iwlan.IwlanHelper;
import com.google.android.iwlan.epdg.NaptrDnsResolver.NaptrTarget;
@@ -51,32 +54,79 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
public class EpdgSelector {
private static final String TAG = "EpdgSelector";
- private Context mContext;
- private int mSlotId;
- private static ConcurrentHashMap<Integer, EpdgSelector> mSelectorInstances =
+ private final Context mContext;
+ private final int mSlotId;
+ private static final ConcurrentHashMap<Integer, EpdgSelector> mSelectorInstances =
new ConcurrentHashMap<>();
private int mV4PcoId = -1;
private int mV6PcoId = -1;
private byte[] mV4PcoData = null;
private byte[] mV6PcoData = null;
+ @NonNull private final ErrorPolicyManager mErrorPolicyManager;
+
+ // 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_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<>(
+ MAX_DNS_RESOLVER_THREADS
+ * MAX_EPDG_SELECTION_THREADS
+ * NUM_EPDG_SELECTION_EXECUTORS);
+
+ Executor mDnsResolutionExecutor =
+ new ThreadPoolExecutor(
+ 0, MAX_DNS_RESOLVER_THREADS, 60L, TimeUnit.SECONDS, dnsResolutionQueue);
+
+ ExecutorService mEpdgSelectionExecutor =
+ new ThreadPoolExecutor(
+ 0,
+ MAX_EPDG_SELECTION_THREADS,
+ 60L,
+ TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
+ Future mDnsPrefetchFuture;
+
+ ExecutorService mSosEpdgSelectionExecutor =
+ new ThreadPoolExecutor(
+ 0,
+ MAX_EPDG_SELECTION_THREADS,
+ 60L,
+ TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
+ Future mSosDnsPrefetchFuture;
final Comparator<InetAddress> inetAddressComparator =
- new Comparator<InetAddress>() {
- @Override
- public int compare(InetAddress ip1, InetAddress ip2) {
- if ((ip1 instanceof Inet4Address) && (ip2 instanceof Inet6Address)) {
- return -1;
- } else if ((ip1 instanceof Inet6Address) && (ip2 instanceof Inet4Address)) {
- return 1;
- } else {
- return 0;
- }
+ (ip1, ip2) -> {
+ if ((ip1 instanceof Inet4Address) && (ip2 instanceof Inet6Address)) {
+ return -1;
+ } else if ((ip1 instanceof Inet6Address) && (ip2 instanceof Inet4Address)) {
+ return 1;
+ } else {
+ return 0;
}
};
@@ -87,9 +137,16 @@ public class EpdgSelector {
@IntDef({PROTO_FILTER_IPV4, PROTO_FILTER_IPV6, PROTO_FILTER_IPV4V6})
@interface ProtoFilter {}
+ public static final int IPV4_PREFERRED = 0;
+ public static final int IPV6_PREFERRED = 1;
+ public static final int SYSTEM_PREFERRED = 2;
+
+ @IntDef({IPV4_PREFERRED, IPV6_PREFERRED, SYSTEM_PREFERRED})
+ @interface EpdgAddressOrder {}
+
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);
}
@@ -98,6 +155,8 @@ public class EpdgSelector {
EpdgSelector(Context context, int slotId) {
mContext = context;
mSlotId = slotId;
+
+ mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId);
}
public static EpdgSelector getSelectorInstance(Context context, int slotId) {
@@ -106,7 +165,12 @@ public class EpdgSelector {
}
public boolean setPcoData(int pcoId, byte[] pcoData) {
- Log.d(TAG, "onReceive PcoId:" + String.format("0x%04x", pcoId) + " PcoData:" + pcoData);
+ Log.d(
+ TAG,
+ "onReceive PcoId:"
+ + String.format("0x%04x", pcoId)
+ + " PcoData:"
+ + Arrays.toString(pcoData));
int PCO_ID_IPV6 =
IwlanHelper.getConfig(
@@ -143,69 +207,242 @@ public class EpdgSelector {
mV6PcoData = null;
}
+ private CompletableFuture<Map.Entry<String, List<InetAddress>>> submitDnsResolverQuery(
+ String domainName, Network network, int queryType, Executor executor) {
+ CompletableFuture<Map.Entry<String, List<InetAddress>>> result = new CompletableFuture();
+
+ final DnsResolver.Callback<List<InetAddress>> cb =
+ new DnsResolver.Callback<List<InetAddress>>() {
+ @Override
+ public void onAnswer(@NonNull final List<InetAddress> answer, final int rcode) {
+ if (rcode != 0) {
+ Log.e(
+ TAG,
+ "DnsResolver Response Code = "
+ + rcode
+ + " for domain "
+ + domainName);
+ }
+ Map.Entry<String, List<InetAddress>> entry = Map.entry(domainName, answer);
+ result.complete(entry);
+ }
+
+ @Override
+ public void onError(@Nullable final DnsResolver.DnsException error) {
+ Log.e(
+ TAG,
+ "Resolve DNS with error: " + error + " for domain: " + domainName);
+ result.complete(null);
+ }
+ };
+ DnsResolver.getInstance()
+ .query(network, domainName, queryType, DnsResolver.FLAG_EMPTY, executor, null, cb);
+ return result;
+ }
+
+ private List<InetAddress> v4v6ProtocolFilter(List<InetAddress> ipList, int filter) {
+ List<InetAddress> validIpList = new ArrayList<>();
+ for (InetAddress ipAddress : ipList) {
+ if (IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) {
+ continue;
+ }
+ switch (filter) {
+ case PROTO_FILTER_IPV4:
+ if (ipAddress instanceof Inet4Address) {
+ validIpList.add(ipAddress);
+ }
+ break;
+ case PROTO_FILTER_IPV6:
+ if (ipAddress instanceof Inet6Address) {
+ validIpList.add(ipAddress);
+ }
+ break;
+ case PROTO_FILTER_IPV4V6:
+ validIpList.add(ipAddress);
+ break;
+ default:
+ Log.d(TAG, "Invalid ProtoFilter : " + filter);
+ }
+ }
+ return validIpList;
+ }
+
+ // Converts a list of CompletableFutures of type T into a single CompletableFuture containing a
+ // list of T. The resulting CompletableFuture waits for all futures to complete,
+ // even if any future throw an exception.
+ private <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futuresList) {
+ CompletableFuture<Void> allFuturesResult =
+ CompletableFuture.allOf(
+ futuresList.toArray(new CompletableFuture[futuresList.size()]));
+ return allFuturesResult.thenApply(
+ v ->
+ futuresList.stream()
+ .map(CompletableFuture::join)
+ .filter(Objects::nonNull)
+ .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.
+ *
+ * @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, 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<>());
+ // 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(timeout, TimeUnit.SECONDS);
+ } catch (ExecutionException e) {
+ Log.e(TAG, "Cause of ExecutionException: ", e.getCause());
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.e(TAG, "InterruptedException: ", e);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "TimeoutException: ", e);
+ } finally {
+ if (resultList == null) {
+ Log.w(TAG, "No IP addresses in parallel DNS query!");
+ } else {
+ for (Map.Entry<String, List<InetAddress>> entry : resultList) {
+ 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);
+ }
+ }
+ }
+ return domainNameToIpAddr;
+ }
+
+ /**
+ * Updates the validIpList with the IP addresses corresponding to this domainName. Runs blocking
+ * DNS resolution on the same thread.
+ *
+ * @param domainName Domain name for which DNS resolution needs to be performed.
+ * @param filter Selects for IPv4, IPv6 (or both) addresses from the resulting DNS records
+ * @param validIpList A running list of IP addresses that needs to be updated.
+ * @param network {@link Network} Network on which to run the DNS query.
+ */
private void getIP(
- String domainName, int filter, ArrayList<InetAddress> validIpList, Network network) {
- InetAddress[] ipList;
+ String domainName, int filter, List<InetAddress> validIpList, Network network) {
+ List<InetAddress> ipList = new ArrayList<InetAddress>();
// Get All IP for each domain name
Log.d(TAG, "Input domainName : " + domainName);
- try {
- CompletableFuture<List<InetAddress>> result = new CompletableFuture();
- final DnsResolver.Callback<List<InetAddress>> cb =
- new DnsResolver.Callback<List<InetAddress>>() {
- @Override
- public void onAnswer(
- @NonNull final List<InetAddress> answer, final int rcode) {
- if (rcode != 0) {
- Log.e(TAG, "DnsResolver Response Code = " + rcode);
+
+ if (InetAddresses.isNumericAddress(domainName)) {
+ Log.d(TAG, domainName + " is a numeric IP address!");
+ ipList.add(InetAddresses.parseNumericAddress(domainName));
+ } else {
+ try {
+ CompletableFuture<List<InetAddress>> result = new CompletableFuture();
+ final DnsResolver.Callback<List<InetAddress>> cb =
+ new DnsResolver.Callback<List<InetAddress>>() {
+ @Override
+ public void onAnswer(
+ @NonNull final List<InetAddress> answer, final int rcode) {
+ if (rcode != 0) {
+ Log.e(TAG, "DnsResolver Response Code = " + rcode);
+ }
+ result.complete(answer);
}
- result.complete(answer);
- }
- @Override
- public void onError(@Nullable final DnsResolver.DnsException error) {
- Log.e(TAG, "Resolve DNS with error : " + error);
- result.completeExceptionally(error);
- }
- };
- DnsResolver.getInstance()
- .query(network, domainName, DnsResolver.FLAG_EMPTY, r -> r.run(), null, cb);
-
- // Filter the IP list by input ProtoFilter
- for (InetAddress ipAddress : result.get()) {
- switch (filter) {
- case PROTO_FILTER_IPV4:
- if (ipAddress instanceof Inet4Address) {
- validIpList.add(ipAddress);
- }
- break;
- case PROTO_FILTER_IPV6:
- if (!IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) {
- validIpList.add(ipAddress);
- }
- break;
- case PROTO_FILTER_IPV4V6:
- validIpList.add(ipAddress);
- break;
- default:
- Log.d(TAG, "Invalid ProtoFilter : " + filter);
+ @Override
+ public void onError(@Nullable final DnsResolver.DnsException error) {
+ Log.e(TAG, "Resolve DNS with error : " + error);
+ result.completeExceptionally(error);
+ }
+ };
+ DnsResolver.getInstance()
+ .query(
+ network,
+ domainName,
+ DnsResolver.FLAG_EMPTY,
+ Runnable::run,
+ null,
+ cb);
+ ipList =
+ new ArrayList<>(
+ result.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS));
+ } catch (ExecutionException e) {
+ Log.e(TAG, "Cause of ExecutionException: ", e.getCause());
+ } catch (InterruptedException e) {
+ Thread thread = Thread.currentThread();
+ if (thread.interrupted()) {
+ thread.interrupt();
}
+ Log.e(TAG, "InterruptedException: ", e);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "TimeoutException: ", e);
}
- } catch (Exception e) {
- Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e);
}
+
+ List<InetAddress> filteredIpList = v4v6ProtocolFilter(ipList, filter);
+ validIpList.addAll(filteredIpList);
}
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
@@ -223,59 +460,72 @@ 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 =
- new StringBuilder()
- .append(subInfo.getMccString())
- .append("-")
- .append(subInfo.getMncString())
- .toString();
- 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 ArrayList<InetAddress> removeDuplicateIp(ArrayList<InetAddress> validIpList) {
- ArrayList<InetAddress> resultIpList = new ArrayList<InetAddress>();
+ private List<String> getPlmnsFromCarrierConfig() {
+ return Arrays.asList(
+ IwlanHelper.getConfig(
+ CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, mContext, mSlotId));
+ }
- for (Iterator<InetAddress> iterator = validIpList.iterator(); iterator.hasNext(); ) {
- InetAddress validIp = iterator.next();
+ 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>();
+
+ for (InetAddress validIp : validIpList) {
if (!resultIpList.contains(validIp)) {
resultIpList.add(validIp);
}
@@ -284,16 +534,51 @@ public class EpdgSelector {
return resultIpList;
}
+ private void prioritizeIp(@NonNull List<InetAddress> validIpList, @EpdgAddressOrder int order) {
+ switch (order) {
+ case IPV4_PREFERRED:
+ validIpList.sort(inetAddressComparator);
+ break;
+ case IPV6_PREFERRED:
+ validIpList.sort(inetAddressComparator.reversed());
+ break;
+ case SYSTEM_PREFERRED:
+ break;
+ default:
+ Log.w(TAG, "Invalid EpdgAddressOrder : " + order);
+ }
+ }
+
private String[] splitMccMnc(String plmn) {
String[] mccmnc = plmn.split("-");
mccmnc[1] = String.format("%03d", Integer.parseInt(mccmnc[1]));
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 =
- mTelephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ Objects.requireNonNull(mTelephonyManager)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
if (mTelephonyManager == null) {
Log.e(TAG, "TelephonyManager is NULL");
@@ -304,14 +589,14 @@ public class EpdgSelector {
}
private void resolutionMethodStatic(
- int filter, ArrayList<InetAddress> validIpList, boolean isRoaming, Network network) {
+ int filter, List<InetAddress> validIpList, Network network) {
String[] domainNames = null;
Log.d(TAG, "STATIC Method");
// Get the static domain names from carrier config
// Config obtained in form of a list of domain names separated by
- // a delimeter is only used for testing purpose.
+ // a delimiter is only used for testing purpose.
if (!inSameCountry()) {
domainNames =
getDomainNames(
@@ -327,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) {
@@ -345,7 +635,9 @@ public class EpdgSelector {
boolean inSameCountry = true;
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- tm = tm.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ tm =
+ Objects.requireNonNull(tm)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
if (tm != null) {
String simCountry = tm.getSimCountryIso();
@@ -359,14 +651,15 @@ public class EpdgSelector {
return inSameCountry;
}
- private void resolutionMethodPlmn(
- int filter, ArrayList<InetAddress> validIpList, boolean isEmergency, Network network) {
+ private Map<String, List<InetAddress>> resolutionMethodPlmn(
+ int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) {
String[] plmnList;
StringBuilder domainName = new StringBuilder();
Log.d(TAG, "PLMN Method");
plmnList = getPlmnList();
+ List<String> domainNames = new ArrayList<>();
for (String plmn : plmnList) {
String[] mccmnc = splitMccMnc(plmn);
/*
@@ -377,22 +670,38 @@ public class EpdgSelector {
* sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org
*/
if (isEmergency) {
- domainName.append("sos.");
+ domainName = new StringBuilder();
+ domainName
+ .append("sos.")
+ .append("epdg.epc.mnc")
+ .append(mccmnc[1])
+ .append(".mcc")
+ .append(mccmnc[0])
+ .append(".pub.3gppnetwork.org");
+ domainNames.add(domainName.toString());
+ domainName.setLength(0);
}
-
+ // For emergency PDN setup, still adding FQDN without "sos" header as second priority
+ // because some operator doesn't support hostname with "sos" prefix.
domainName
.append("epdg.epc.mnc")
.append(mccmnc[1])
.append(".mcc")
.append(mccmnc[0])
.append(".pub.3gppnetwork.org");
- getIP(domainName.toString(), filter, validIpList, network);
+ domainNames.add(domainName.toString());
domainName.setLength(0);
}
+
+ LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr =
+ getIP(domainNames, filter, network, PARALLEL_PLMN_RESOLUTION_TIMEOUT_DURATION_SEC);
+ printParallelDnsResult(domainNameToIpAddr);
+ domainNameToIpAddr.values().forEach(validIpList::addAll);
+ return domainNameToIpAddr;
}
private void resolutionMethodCellularLoc(
- int filter, ArrayList<InetAddress> validIpList, boolean isEmergency, Network network) {
+ int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) {
String[] plmnList;
StringBuilder domainName = new StringBuilder();
@@ -400,7 +709,8 @@ public class EpdgSelector {
TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
mTelephonyManager =
- mTelephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ Objects.requireNonNull(mTelephonyManager)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
if (mTelephonyManager == null) {
Log.e(TAG, "TelephonyManager is NULL");
@@ -466,8 +776,7 @@ public class EpdgSelector {
domainName.setLength(0);
}
} else if (cellInfo instanceof CellInfoNr) {
- CellIdentityNr nrCellId =
- (CellIdentityNr) ((CellInfoNr) cellInfo).getCellIdentity();
+ CellIdentityNr nrCellId = (CellIdentityNr) cellInfo.getCellIdentity();
String tacString = String.format("%06x", nrCellId.getTac());
String[] tacSubString = new String[3];
tacSubString[0] = tacString.substring(0, 2);
@@ -514,7 +823,7 @@ public class EpdgSelector {
private void lacDomainNameResolution(
int filter,
- ArrayList<InetAddress> validIpList,
+ List<InetAddress> validIpList,
String lacString,
boolean isEmergency,
Network network) {
@@ -548,7 +857,7 @@ public class EpdgSelector {
}
}
- private void resolutionMethodPco(int filter, ArrayList<InetAddress> validIpList) {
+ private void resolutionMethodPco(int filter, List<InetAddress> validIpList) {
Log.d(TAG, "PCO Method");
int PCO_ID_IPV6 =
@@ -586,7 +895,7 @@ public class EpdgSelector {
}
}
- private void getInetAddressWithPcoData(byte[] pcoData, ArrayList<InetAddress> validIpList) {
+ private void getInetAddressWithPcoData(byte[] pcoData, List<InetAddress> validIpList) {
InetAddress ipAddress;
if (pcoData != null && pcoData.length > 0) {
try {
@@ -647,7 +956,7 @@ public class EpdgSelector {
private void processNaptrResponse(
int filter,
- ArrayList<InetAddress> validIpList,
+ List<InetAddress> validIpList,
boolean isEmergency,
Network network,
boolean isRegisteredWith3GPP,
@@ -699,12 +1008,13 @@ public class EpdgSelector {
}
private void resolutionMethodVisitedCountry(
- int filter, ArrayList<InetAddress> validIpList, boolean isEmergency, Network network) {
+ int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) {
StringBuilder domainName = new StringBuilder();
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
telephonyManager =
- telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ Objects.requireNonNull(telephonyManager)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
if (telephonyManager == null) {
Log.e(TAG, "TelephonyManager is NULL");
@@ -724,8 +1034,7 @@ public class EpdgSelector {
final String cellMcc = telephonyManager.getNetworkOperator().substring(0, 3);
final String cellMnc = telephonyManager.getNetworkOperator().substring(3);
- final String plmnFromNetwork =
- new StringBuilder().append(cellMcc).append("-").append(cellMnc).toString();
+ final String plmnFromNetwork = cellMcc + "-" + cellMnc;
final String registeredhostName = composeFqdnWithMccMnc(cellMcc, cellMnc, isEmergency);
/*
@@ -755,7 +1064,7 @@ public class EpdgSelector {
.append(cellMcc)
.append(".visited-country.pub.3gppnetwork.org");
- Log.d(TAG, "Visited Country FQDN with " + domainName.toString());
+ Log.d(TAG, "Visited Country FQDN with " + domainName);
CompletableFuture<List<NaptrTarget>> naptrDnsResult = new CompletableFuture<>();
DnsResolver.Callback<List<NaptrTarget>> naptrDnsCb =
@@ -774,10 +1083,11 @@ public class EpdgSelector {
naptrDnsResult.completeExceptionally(error);
}
};
- NaptrDnsResolver.query(network, domainName.toString(), r -> r.run(), null, naptrDnsCb);
+ NaptrDnsResolver.query(network, domainName.toString(), Runnable::run, null, naptrDnsCb);
try {
- final List<NaptrTarget> naptrResponse = naptrDnsResult.get();
+ final List<NaptrTarget> naptrResponse =
+ naptrDnsResult.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS);
// Check if there is any record in the NAPTR response
if (naptrResponse != null && naptrResponse.size() > 0) {
processNaptrResponse(
@@ -793,27 +1103,74 @@ public class EpdgSelector {
} catch (ExecutionException e) {
Log.e(TAG, "Cause of ExecutionException: ", e.getCause());
} catch (InterruptedException e) {
- if (Thread.currentThread().interrupted()) {
- Thread.currentThread().interrupt();
+ Thread thread = Thread.currentThread();
+ if (thread.interrupted()) {
+ thread.interrupt();
}
Log.e(TAG, "InterruptedException: ", e);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "TimeoutException: ", e);
}
}
+ // Cancels duplicate prefetches a prefetch is already running. Always schedules tunnel bringup.
+ private void trySubmitEpdgSelectionExecutor(
+ Runnable runnable, boolean isPrefetch, boolean isEmergency) {
+ if (isEmergency) {
+ if (isPrefetch) {
+ if (mSosDnsPrefetchFuture == null || mSosDnsPrefetchFuture.isDone()) {
+ mSosDnsPrefetchFuture = mSosEpdgSelectionExecutor.submit(runnable);
+ }
+ } else {
+ mSosEpdgSelectionExecutor.execute(runnable);
+ }
+ } else {
+ if (isPrefetch) {
+ if (mDnsPrefetchFuture == null || mDnsPrefetchFuture.isDone()) {
+ mDnsPrefetchFuture = mEpdgSelectionExecutor.submit(runnable);
+ }
+ } else {
+ mEpdgSelectionExecutor.execute(runnable);
+ }
+ }
+ }
+
+ /**
+ * Asynchronously runs DNS resolution on a carrier-specific list of ePDG servers into IP
+ * addresses, and passes them to the caller via the {@link EpdgSelectorCallback}.
+ *
+ * @param transactionId A unique ID passed in to match the response with the request. If this
+ * value is 0, the caller is not interested in the result.
+ * @param filter Allows the caller to filter for IPv4 or IPv6 servers, or both.
+ * @param isRoaming Specifies whether the subscription is currently in roaming state.
+ * @param isEmergency Specifies whether the ePDG server lookup is to make an emergency call.
+ * @param network {@link Network} The server lookups will be performed over this Network.
+ * @param selectorCallback {@link EpdgSelectorCallback} The result will be returned through this
+ * callback. If null, the caller is not interested in the result. Typically, this means the
+ * caller is performing DNS prefetch of the ePDG server addresses to warm the native
+ * dnsresolver module's caches.
+ * @return {link IwlanError} denoting the status of this operation.
+ */
public IwlanError getValidatedServerList(
int transactionId,
@ProtoFilter int filter,
+ @EpdgAddressOrder int order,
boolean isRoaming,
boolean isEmergency,
@NonNull Network network,
EpdgSelectorCallback selectorCallback) {
- ArrayList<InetAddress> validIpList = new ArrayList<InetAddress>();
- StringBuilder domainName = new StringBuilder();
- Runnable doValidation =
+ final Runnable epdgSelectionRunnable =
() -> {
- Log.d(TAG, "Processing request with transactionId: " + transactionId);
- String[] plmnList;
+ List<InetAddress> validIpList = new ArrayList<>();
+ Log.d(
+ TAG,
+ "Processing request with transactionId: "
+ + transactionId
+ + ", for slotID: "
+ + mSlotId
+ + ", isEmergency: "
+ + isEmergency);
int[] addrResolutionMethods =
IwlanHelper.getConfig(
@@ -834,14 +1191,17 @@ public class EpdgSelector {
resolutionMethodVisitedCountry(filter, validIpList, isEmergency, network);
}
+ Map<String, List<InetAddress>> plmnDomainNamesToIpAddress = null;
for (int addrResolutionMethod : addrResolutionMethods) {
switch (addrResolutionMethod) {
case CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC:
- resolutionMethodStatic(filter, validIpList, isRoaming, network);
+ resolutionMethodStatic(filter, validIpList, network);
break;
case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN:
- resolutionMethodPlmn(filter, validIpList, isEmergency, network);
+ plmnDomainNamesToIpAddress =
+ resolutionMethodPlmn(
+ filter, validIpList, isEmergency, network);
break;
case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PCO:
@@ -862,8 +1222,29 @@ public class EpdgSelector {
}
if (selectorCallback != null) {
+ if (mErrorPolicyManager.getMostRecentDataFailCause()
+ == DataFailCause.IWLAN_CONGESTION) {
+ Objects.requireNonNull(plmnDomainNamesToIpAddress)
+ .values()
+ .removeIf(List::isEmpty);
+
+ int numFqdns = plmnDomainNamesToIpAddress.size();
+ int index = mErrorPolicyManager.getCurrentFqdnIndex(numFqdns);
+ if (index >= 0 && index < numFqdns) {
+ Object[] keys = plmnDomainNamesToIpAddress.keySet().toArray();
+ validIpList = plmnDomainNamesToIpAddress.get((String) keys[index]);
+ } else {
+ Log.w(
+ TAG,
+ "CONGESTION error handling- invalid index: "
+ + index
+ + " number of PLMN FQDNs: "
+ + numFqdns);
+ }
+ }
+
if (!validIpList.isEmpty()) {
- Collections.sort(validIpList, inetAddressComparator);
+ prioritizeIp(validIpList, order);
selectorCallback.onServerListChanged(
transactionId, removeDuplicateIp(validIpList));
} else {
@@ -874,8 +1255,20 @@ public class EpdgSelector {
}
}
};
- Thread subThread = new Thread(doValidation);
- subThread.start();
+
+ boolean isPrefetch = (selectorCallback == null);
+ trySubmitEpdgSelectionExecutor(epdgSelectionRunnable, isPrefetch, isEmergency);
+
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 ea78893..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;
@@ -50,6 +51,7 @@ import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.SaProposal;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeIOException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.ipsec.ike.ike3gpp.Ike3gppBackoffTimer;
import android.net.ipsec.ike.ike3gpp.Ike3gppData;
@@ -74,9 +76,12 @@ import com.android.internal.annotations.VisibleForTesting;
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.TunnelMetricsInterface;
+import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics;
+import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics;
import com.google.android.iwlan.exceptions.IwlanSimNotReadyException;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Inet4Address;
@@ -85,11 +90,10 @@ import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -99,9 +103,8 @@ import java.util.concurrent.TimeUnit;
public class EpdgTunnelManager {
- private Context mContext;
+ private final Context mContext;
private final int mSlotId;
- private HandlerThread mHandlerThread;
private Handler mHandler;
private static final int EVENT_TUNNEL_BRINGUP_REQUEST = 0;
@@ -115,6 +118,7 @@ public class EpdgTunnelManager {
private static final int EVENT_UPDATE_NETWORK = 9;
private static final int EVENT_IKE_SESSION_OPENED = 10;
private static final int EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED = 11;
+ private static final int EVENT_IKE_3GPP_DATA_RECEIVED = 12;
private static final int IKE_HARD_LIFETIME_SEC_MINIMUM = 300;
private static final int IKE_HARD_LIFETIME_SEC_MAXIMUM = 86400;
private static final int IKE_SOFT_LIFETIME_SEC_MINIMUM = 120;
@@ -143,26 +147,35 @@ public class EpdgTunnelManager {
private static final String TRAFFIC_SELECTOR_IPV6_END_ADDR =
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
- private static Map<Integer, EpdgTunnelManager> mTunnelManagerInstances =
+ // "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> mRequestQueue = new LinkedList<>();
+ private Queue<TunnelRequestWrapper> mPendingBringUpRequests = new LinkedList<>();
+
+ private final EpdgInfo mValidEpdgInfo = new EpdgInfo();
+ @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 EpdgInfo mValidEpdgInfo = new EpdgInfo();
- private InetAddress mEpdgAddress;
- private Network mNetwork;
private int mTransactionId = 0;
- private int mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
- private boolean mIsEpdgAddressSelected;
- private IkeSessionCreator mIkeSessionCreator;
+ private boolean mHasConnectedToEpdg;
+ private final IkeSessionCreator mIkeSessionCreator;
private Map<String, TunnelConfig> mApnNameToTunnelConfig = new ConcurrentHashMap<>();
+ private final Map<String, Integer> mApnNameToCurrentToken = new ConcurrentHashMap<>();
private final String TAG;
- private List<InetAddress> mLocalAddresses;
-
@Nullable private byte[] mNextReauthId = null;
+ private long mEpdgServerSelectionDuration = 0;
+ private long mEpdgServerSelectionStartTime = 0;
+ private long mIkeTunnelEstablishmentStartTime = 0;
private static final Set<Integer> VALID_DH_GROUPS;
private static final Set<Integer> VALID_KEY_LENGTHS;
@@ -171,60 +184,49 @@ public class EpdgTunnelManager {
private static final Set<Integer> VALID_ENCRYPTION_ALGOS;
private static final String CONFIG_TYPE_DH_GROUP = "dh group";
- private static final String CONFIG_TYPE_KEY_LEN = "alogrithm key length";
+ private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length";
private static final String CONFIG_TYPE_PRF_ALGO = "prf algorithm";
private static final String CONFIG_TYPE_INTEGRITY_ALGO = "integrity algorithm";
private static final String CONFIG_TYPE_ENCRYPT_ALGO = "encryption algorithm";
static {
VALID_DH_GROUPS =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- SaProposal.DH_GROUP_1024_BIT_MODP,
- SaProposal.DH_GROUP_1536_BIT_MODP,
- SaProposal.DH_GROUP_2048_BIT_MODP)));
+ Set.of(
+ SaProposal.DH_GROUP_1024_BIT_MODP,
+ SaProposal.DH_GROUP_1536_BIT_MODP,
+ SaProposal.DH_GROUP_2048_BIT_MODP);
VALID_KEY_LENGTHS =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- SaProposal.KEY_LEN_AES_128,
- SaProposal.KEY_LEN_AES_192,
- SaProposal.KEY_LEN_AES_256)));
+ Set.of(
+ SaProposal.KEY_LEN_AES_128,
+ SaProposal.KEY_LEN_AES_192,
+ SaProposal.KEY_LEN_AES_256);
VALID_ENCRYPTION_ALGOS =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
- SaProposal.ENCRYPTION_ALGORITHM_AES_CTR)));
+ Set.of(
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
+ SaProposal.ENCRYPTION_ALGORITHM_AES_CTR);
VALID_INTEGRITY_ALGOS =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
- SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
- SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256)));
+ Set.of(
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+ SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+ SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
VALID_PRF_ALGOS =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
- SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
- SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
- SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
- SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512)));
+ Set.of(
+ SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+ SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+ SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512);
}
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);
}
@@ -238,17 +240,28 @@ public class EpdgTunnelManager {
@VisibleForTesting
class TunnelConfig {
@NonNull final TunnelCallback mTunnelCallback;
+ @NonNull final TunnelMetricsInterface mTunnelMetrics;
// TODO: Change this to TunnelLinkProperties after removing autovalue
private List<InetAddress> mPcscfAddrList;
private List<InetAddress> mDnsAddrList;
private List<LinkAddress> mInternalAddrList;
- private InetAddress mSrcIpv6Address;
- private int mSrcIpv6AddressPrefixLen;
+ private final InetAddress mSrcIpv6Address;
+ private final int mSrcIpv6AddressPrefixLen;
private NetworkSliceInfo mSliceInfo;
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;
}
@@ -277,13 +290,17 @@ public class EpdgTunnelManager {
public TunnelConfig(
IkeSession ikeSession,
TunnelCallback tunnelCallback,
+ TunnelMetricsInterface tunnelMetrics,
InetAddress srcIpv6Addr,
int srcIpv6PrefixLength) {
mTunnelCallback = tunnelCallback;
+ mTunnelMetrics = tunnelMetrics;
mIkeSession = ikeSession;
mError = new IwlanError(IwlanError.NO_ERROR);
mSrcIpv6Address = srcIpv6Addr;
mSrcIpv6AddressPrefixLen = srcIpv6PrefixLength;
+
+ setIkeSessionState(IkeSessionState.IKE_SESSION_INIT_IN_PROGRESS);
}
@NonNull
@@ -291,6 +308,11 @@ public class EpdgTunnelManager {
return mTunnelCallback;
}
+ @NonNull
+ TunnelMetricsInterface getTunnelMetrics() {
+ return mTunnelMetrics;
+ }
+
List<InetAddress> getPcscfAddrList() {
return mPcscfAddrList;
}
@@ -315,9 +337,7 @@ public class EpdgTunnelManager {
if (laddr.isIpv6() && (laddr.getPrefixLength() == mSrcIpv6AddressPrefixLen)) {
IpPrefix assignedPrefix = new IpPrefix(laddr.getAddress(), laddr.getPrefixLength());
IpPrefix srcPrefix = new IpPrefix(mSrcIpv6Address, mSrcIpv6AddressPrefixLen);
- if (assignedPrefix.equals(srcPrefix)) {
- return true;
- }
+ return assignedPrefix.equals(srcPrefix);
}
return false;
}
@@ -325,7 +345,7 @@ public class EpdgTunnelManager {
public void setInternalAddrList(List<LinkAddress> internalAddrList) {
mInternalAddrList = new ArrayList<LinkAddress>(internalAddrList);
if (getSrcIpv6Address() != null) {
- // check if we can reuse src ipv6 address (i.e if prefix is same)
+ // check if we can reuse src ipv6 address (i.e. if prefix is same)
for (LinkAddress assignedAddr : internalAddrList) {
if (isPrefixSameAsSrcIP(assignedAddr)) {
// the assigned IPv6 address is same as pre-Handover IPv6
@@ -374,21 +394,6 @@ public class EpdgTunnelManager {
return mSrcIpv6Address;
}
- public int getSrcIpv6AddressPrefixLen() {
- return mSrcIpv6AddressPrefixLen;
- }
-
- private String addressListString(List<InetAddress> list) {
- StringBuilder sb = new StringBuilder();
- sb.append("{ ");
- for (InetAddress addr : list) {
- sb.append(addr);
- sb.append(", ");
- }
- sb.append(" }");
- return sb.toString();
- }
-
public boolean hasTunnelOpened() {
return mInternalAddrList != null
&& !mInternalAddrList.isEmpty() /* The child session is opened */
@@ -399,34 +404,13 @@ public class EpdgTunnelManager {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("TunnelConfig { ");
- /*if (mPcscfAddrList != null) {
- sb.append("mPcscfAddrList: " + addressListString(mPcscfAddrList));
- sb.append(", ");
- }
- if (mDnsAddrList != null) {
- sb.append("mDnsAddrList: " + addressListString(mDnsAddrList));
- sb.append(", ");
- }
- if (mInternalAddrList != null) {
- sb.append("mInternalAddrList: { ");
- for (LinkAddress addr : mInternalAddrList) {
- sb.append(addr + ", ");
- }
- sb.append(" }, ");
- }
-
- if (mSrcIpv6Address != null) {
- sb.append("{mSrcIpv6Address: " + mSrcIpv6Address + "}, ");
- } else {
- sb.append("{NULL mSrcIpv6Address}, ");
- } */
if (mSliceInfo != null) {
- sb.append("mSliceInfo: " + mSliceInfo + ", ");
+ sb.append("mSliceInfo: ").append(mSliceInfo).append(", ");
}
if (mIsBackoffTimeValid) {
- sb.append("mBackoffTime: " + mBackoffTime + ", ");
+ sb.append("mBackoffTime: ").append(mBackoffTime).append(", ");
}
sb.append(" }");
return sb.toString();
@@ -437,38 +421,40 @@ public class EpdgTunnelManager {
class TmIkeSessionCallback implements IkeSessionCallback {
private final String mApnName;
+ private final int mToken;
- TmIkeSessionCallback(String apnName) {
+ TmIkeSessionCallback(String apnName, int token) {
this.mApnName = apnName;
+ this.mToken = token;
}
@Override
public void onOpened(IkeSessionConfiguration sessionConfiguration) {
- Log.d(TAG, "Ike session opened for apn: " + mApnName);
+ Log.d(TAG, "Ike session opened for apn: " + mApnName + " with token: " + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IKE_SESSION_OPENED,
- new IkeSessionOpenedData(mApnName, sessionConfiguration)));
+ new IkeSessionOpenedData(mApnName, mToken, sessionConfiguration)));
}
@Override
public void onClosed() {
- Log.d(TAG, "Ike session closed for apn: " + mApnName);
+ Log.d(TAG, "Ike session closed for apn: " + mApnName + " with token: " + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IKE_SESSION_CLOSED,
- new SessionClosedData(mApnName, new IwlanError(IwlanError.NO_ERROR))));
+ new SessionClosedData(mApnName, mToken, null /* ikeException */)));
}
@Override
- public void onClosedExceptionally(IkeException exception) {
+ public void onClosedWithException(IkeException exception) {
mNextReauthId = null;
- onSessionClosedWithException(exception, mApnName, EVENT_IKE_SESSION_CLOSED);
+ onSessionClosedWithException(exception, mApnName, mToken, EVENT_IKE_SESSION_CLOSED);
}
@Override
public void onError(IkeProtocolException exception) {
- Log.d(TAG, "Ike session onError for apn: " + mApnName);
+ Log.d(TAG, "Ike session onError for apn: " + mApnName + " with token: " + mToken);
mNextReauthId = null;
@@ -488,51 +474,34 @@ public class EpdgTunnelManager {
TAG,
"Ike session connection info changed for apn: "
+ mApnName
+ + " with token: "
+ + mToken
+ " Network: "
+ network);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED,
- new IkeSessionConnectionInfoData(mApnName, ikeSessionConnectionInfo)));
+ new IkeSessionConnectionInfoData(
+ mApnName, mToken, ikeSessionConnectionInfo)));
}
}
@VisibleForTesting
class TmIke3gppCallback implements Ike3gppExtension.Ike3gppDataListener {
private final String mApnName;
+ private final int mToken;
- private TmIke3gppCallback(String apnName) {
+ private TmIke3gppCallback(String apnName, int token) {
mApnName = apnName;
+ mToken = token;
}
@Override
public void onIke3gppDataReceived(List<Ike3gppData> payloads) {
- if (payloads != null && !payloads.isEmpty()) {
- TunnelConfig tunnelConfig = mApnNameToTunnelConfig.get(mApnName);
- for (Ike3gppData payload : payloads) {
- if (payload.getDataType() == DATA_TYPE_NOTIFY_N1_MODE_INFORMATION) {
- Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_N1_MODE_INFORMATION");
- NetworkSliceInfo si =
- NetworkSliceSelectionAssistanceInformation.getSliceInfo(
- ((Ike3gppN1ModeInformation) payload).getSnssai());
- if (si != null) {
- tunnelConfig.setSliceInfo(si);
- Log.d(TAG, "SliceInfo: " + si);
- }
- } else if (payload.getDataType() == DATA_TYPE_NOTIFY_BACKOFF_TIMER) {
- Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_BACKOFF_TIMER");
- long backoffTime =
- decodeBackoffTime(
- ((Ike3gppBackoffTimer) payload).getBackoffTimer());
- if (backoffTime > 0) {
- tunnelConfig.setBackoffTime(backoffTime);
- Log.d(TAG, "Backoff Timer: " + backoffTime);
- }
- }
- }
- } else {
- Log.e(TAG, "Null or empty payloads received:");
- }
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ EVENT_IKE_3GPP_DATA_RECEIVED,
+ new Ike3gppDataReceived(mApnName, mToken, payloads)));
}
}
@@ -540,67 +509,93 @@ public class EpdgTunnelManager {
class TmChildSessionCallback implements ChildSessionCallback {
private final String mApnName;
+ private final int mToken;
- TmChildSessionCallback(String apnName) {
+ TmChildSessionCallback(String apnName, int token) {
this.mApnName = apnName;
+ this.mToken = token;
}
@Override
public void onOpened(ChildSessionConfiguration sessionConfiguration) {
+ Log.d(TAG, "onOpened child session for apn: " + mApnName + " with token: " + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_CHILD_SESSION_OPENED,
new TunnelOpenedData(
mApnName,
+ mToken,
sessionConfiguration.getInternalDnsServers(),
sessionConfiguration.getInternalAddresses())));
}
@Override
public void onClosed() {
- Log.d(TAG, "onClosed child session for apn: " + mApnName);
+ Log.d(TAG, "onClosed child session for apn: " + mApnName + " with token: " + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_CHILD_SESSION_CLOSED,
- new SessionClosedData(mApnName, new IwlanError(IwlanError.NO_ERROR))));
+ new SessionClosedData(mApnName, mToken, null /* ikeException */)));
}
@Override
- public void onClosedExceptionally(IkeException exception) {
- onSessionClosedWithException(exception, mApnName, EVENT_CHILD_SESSION_CLOSED);
+ public void onClosedWithException(IkeException exception) {
+ onSessionClosedWithException(exception, mApnName, mToken, EVENT_CHILD_SESSION_CLOSED);
}
@Override
public void onIpSecTransformsMigrated(
IpSecTransform inIpSecTransform, IpSecTransform outIpSecTransform) {
// migration is similar to addition
- Log.d(TAG, "Transforms migrated for apn: + " + mApnName);
+ Log.d(TAG, "Transforms migrated for apn: " + mApnName + " with token: " + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IPSEC_TRANSFORM_CREATED,
new IpsecTransformData(
- inIpSecTransform, IpSecManager.DIRECTION_IN, mApnName)));
+ inIpSecTransform,
+ IpSecManager.DIRECTION_IN,
+ mApnName,
+ mToken)));
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IPSEC_TRANSFORM_CREATED,
new IpsecTransformData(
- outIpSecTransform, IpSecManager.DIRECTION_OUT, mApnName)));
+ outIpSecTransform,
+ IpSecManager.DIRECTION_OUT,
+ mApnName,
+ mToken)));
}
@Override
public void onIpSecTransformCreated(IpSecTransform ipSecTransform, int direction) {
- Log.d(TAG, "Transform created, direction: " + direction + ", apn:" + mApnName);
+ Log.d(
+ TAG,
+ "Transform created, direction: "
+ + direction
+ + ", apn: "
+ + mApnName
+ + ", token: "
+ + mToken);
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_IPSEC_TRANSFORM_CREATED,
- new IpsecTransformData(ipSecTransform, direction, mApnName)));
+ new IpsecTransformData(ipSecTransform, direction, mApnName, mToken)));
}
@Override
public void onIpSecTransformDeleted(IpSecTransform ipSecTransform, int direction) {
- Log.d(TAG, "Transform deleted, direction: " + direction + ", apn:" + mApnName);
+ Log.d(
+ TAG,
+ "Transform deleted, direction: "
+ + direction
+ + ", apn: "
+ + mApnName
+ + ", token: "
+ + mToken);
mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_IPSEC_TRANSFORM_DELETED, ipSecTransform));
+ mHandler.obtainMessage(
+ EVENT_IPSEC_TRANSFORM_DELETED,
+ new IpsecTransformData(ipSecTransform, direction, mApnName, mToken)));
}
}
@@ -619,13 +614,13 @@ public class EpdgTunnelManager {
@VisibleForTesting
Looper getLooper() {
- mHandlerThread = new HandlerThread("EpdgTunnelManagerThread");
- mHandlerThread.start();
- return mHandlerThread.getLooper();
+ HandlerThread handlerThread = new HandlerThread("EpdgTunnelManagerThread");
+ handlerThread.start();
+ return handlerThread.getLooper();
}
/**
- * Gets a epdg tunnel manager instance.
+ * Gets a EpdgTunnelManager instance.
*
* @param context application context
* @param subId subscription ID for the tunnel
@@ -636,6 +631,11 @@ public class EpdgTunnelManager {
subId, k -> new EpdgTunnelManager(context, subId));
}
+ @VisibleForTesting
+ public static void resetAllInstances() {
+ mTunnelManagerInstances.clear();
+ }
+
public interface TunnelCallback {
/**
* Called when the tunnel is opened.
@@ -655,33 +655,36 @@ public class EpdgTunnelManager {
/**
* Close tunnel for an apn. Confirmation of closing will be delivered in TunnelCallback that was
- * provided in {@link #bringUpTunnel}
+ * provided in {@link #bringUpTunnel}. If no tunnel was available, callback will be delivered
+ * using client-provided provided tunnelCallback and iwlanTunnelMetrics
*
* @param apnName apn name
* @param forceClose if true, results in local cleanup of tunnel
- * @return true if params are valid and tunnel exists. False otherwise.
+ * @param tunnelCallback Used if no current or pending IWLAN tunnel exists
+ * @param iwlanTunnelMetrics Used to report metrics if no current or pending IWLAN tunnel exists
*/
- public boolean closeTunnel(@NonNull String apnName, boolean forceClose) {
+ public void closeTunnel(
+ @NonNull String apnName,
+ boolean forceClose,
+ @NonNull TunnelCallback tunnelCallback,
+ @NonNull IwlanTunnelMetricsImpl iwlanTunnelMetrics) {
mHandler.sendMessage(
mHandler.obtainMessage(
EVENT_TUNNEL_BRINGDOWN_REQUEST,
- forceClose ? 1 : 0,
- 0 /*not used*/,
- apnName));
- return true;
+ new TunnelBringdownRequest(
+ apnName, forceClose, tunnelCallback, iwlanTunnelMetrics)));
}
/**
* 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));
}
/**
@@ -694,7 +697,9 @@ public class EpdgTunnelManager {
* @return true if params are valid and no existing tunnel. False otherwise.
*/
public boolean bringUpTunnel(
- @NonNull TunnelSetupRequest setupRequest, @NonNull TunnelCallback tunnelCallback) {
+ @NonNull TunnelSetupRequest setupRequest,
+ @NonNull TunnelCallback tunnelCallback,
+ @NonNull TunnelMetricsInterface tunnelMetrics) {
String apnName = setupRequest.apnName();
if (getTunnelSetupRequestApnName(setupRequest) == null) {
@@ -719,7 +724,7 @@ public class EpdgTunnelManager {
}
TunnelRequestWrapper tunnelRequestWrapper =
- new TunnelRequestWrapper(setupRequest, tunnelCallback);
+ new TunnelRequestWrapper(setupRequest, tunnelCallback, tunnelMetrics);
mHandler.sendMessage(
mHandler.obtainMessage(EVENT_TUNNEL_BRINGUP_REQUEST, tunnelRequestWrapper));
@@ -727,27 +732,33 @@ public class EpdgTunnelManager {
return true;
}
- private void onBringUpTunnel(TunnelSetupRequest setupRequest, TunnelCallback tunnelCallback) {
+ private void onBringUpTunnel(
+ TunnelSetupRequest setupRequest,
+ TunnelCallback tunnelCallback,
+ TunnelMetricsInterface tunnelMetrics) {
String apnName = setupRequest.apnName();
- IkeSessionParams ikeSessionParams = null;
+ IkeSessionParams ikeSessionParams;
Log.d(
TAG,
"Bringing up tunnel for apn: "
+ apnName
- + "ePDG : "
+ + " ePDG: "
+ mEpdgAddress.getHostAddress());
+ final int token = incrementAndGetCurrentTokenForApn(apnName);
+
try {
- ikeSessionParams = buildIkeSessionParams(setupRequest, apnName);
+ ikeSessionParams = buildIkeSessionParams(setupRequest, apnName, token);
} catch (IwlanSimNotReadyException e) {
- mRequestQueue.poll();
IwlanError iwlanError = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION);
reportIwlanError(apnName, iwlanError);
tunnelCallback.onClosed(apnName, iwlanError);
+ tunnelMetrics.onClosed(new OnClosedMetrics.Builder().setApnName(apnName).build());
return;
}
+ mIkeTunnelEstablishmentStartTime = System.currentTimeMillis();
IkeSession ikeSession =
getIkeSessionCreator()
.createIkeSession(
@@ -755,14 +766,15 @@ public class EpdgTunnelManager {
ikeSessionParams,
buildChildSessionParams(setupRequest),
Executors.newSingleThreadExecutor(),
- getTmIkeSessionCallback(apnName),
- new TmChildSessionCallback(apnName));
+ getTmIkeSessionCallback(apnName, token),
+ new TmChildSessionCallback(apnName, token));
boolean isSrcIpv6Present = setupRequest.srcIpv6Address().isPresent();
putApnNameToTunnelConfig(
apnName,
ikeSession,
tunnelCallback,
+ tunnelMetrics,
isSrcIpv6Present ? setupRequest.srcIpv6Address().get() : null,
setupRequest.srcIpv6AddressPrefixLength());
}
@@ -795,9 +807,9 @@ public class EpdgTunnelManager {
private ChildSessionParams buildChildSessionParams(TunnelSetupRequest setupRequest) {
int proto = setupRequest.apnIpProtocol();
int hardTimeSeconds =
- (int) getConfig(CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT);
+ getConfig(CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT);
int softTimeSeconds =
- (int) getConfig(CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT);
+ getConfig(CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT);
if (!isValidChildSessionLifetime(hardTimeSeconds, softTimeSeconds)) {
if (hardTimeSeconds > CHILD_HARD_LIFETIME_SEC_MAXIMUM
&& softTimeSeconds > CHILD_SOFT_LIFETIME_SEC_MINIMUM) {
@@ -805,15 +817,11 @@ public class EpdgTunnelManager {
softTimeSeconds = CHILD_HARD_LIFETIME_SEC_MAXIMUM - LIFETIME_MARGIN_SEC_MINIMUM;
} else {
hardTimeSeconds =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan
- .KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT);
softTimeSeconds =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan
- .KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT);
}
Log.d(
TAG,
@@ -827,7 +835,7 @@ public class EpdgTunnelManager {
new TunnelModeChildSessionParams.Builder()
.setLifetimeSeconds(hardTimeSeconds, softTimeSeconds);
- childSessionParamsBuilder.addSaProposal(buildChildSaProposal());
+ childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal());
boolean handoverIPv4Present = setupRequest.srcIpv4Address().isPresent();
boolean handoverIPv6Present = setupRequest.srcIpv6Address().isPresent();
@@ -898,7 +906,8 @@ public class EpdgTunnelManager {
private @Nullable String getMobileDeviceIdentity() {
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
telephonyManager =
- telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+ Objects.requireNonNull(telephonyManager)
+ .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
if (telephonyManager == null) {
return null;
}
@@ -917,12 +926,13 @@ public class EpdgTunnelManager {
return imei.substring(0, imei.length() - 1) + imeisv_suffix;
}
- private IkeSessionParams buildIkeSessionParams(TunnelSetupRequest setupRequest, String apnName)
+ private IkeSessionParams buildIkeSessionParams(
+ TunnelSetupRequest setupRequest, String apnName, int token)
throws IwlanSimNotReadyException {
int hardTimeSeconds =
- (int) getConfig(CarrierConfigManager.Iwlan.KEY_IKE_REKEY_HARD_TIMER_SEC_INT);
+ getConfig(CarrierConfigManager.Iwlan.KEY_IKE_REKEY_HARD_TIMER_SEC_INT);
int softTimeSeconds =
- (int) getConfig(CarrierConfigManager.Iwlan.KEY_IKE_REKEY_SOFT_TIMER_SEC_INT);
+ getConfig(CarrierConfigManager.Iwlan.KEY_IKE_REKEY_SOFT_TIMER_SEC_INT);
if (!isValidIkeSessionLifetime(hardTimeSeconds, softTimeSeconds)) {
if (hardTimeSeconds > IKE_HARD_LIFETIME_SEC_MAXIMUM
&& softTimeSeconds > IKE_SOFT_LIFETIME_SEC_MINIMUM) {
@@ -930,15 +940,11 @@ public class EpdgTunnelManager {
softTimeSeconds = IKE_HARD_LIFETIME_SEC_MAXIMUM - LIFETIME_MARGIN_SEC_MINIMUM;
} else {
hardTimeSeconds =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan
- .KEY_IKE_REKEY_HARD_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_IKE_REKEY_HARD_TIMER_SEC_INT);
softTimeSeconds =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan
- .KEY_IKE_REKEY_SOFT_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_IKE_REKEY_SOFT_TIMER_SEC_INT);
}
Log.d(
TAG,
@@ -958,8 +964,8 @@ public class EpdgTunnelManager {
.setLocalIdentification(getLocalIdentification())
.setRemoteIdentification(getId(setupRequest.apnName(), false))
.setAuthEap(null, getEapConfig())
- .addSaProposal(buildIkeSaProposal())
- .setNetwork(mNetwork)
+ .addIkeSaProposal(buildIkeSaProposal())
+ .setNetwork(mDefaultNetwork)
.addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
.addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY)
@@ -987,39 +993,51 @@ 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;
- String imei = getMobileDeviceIdentity();
- if (imei != null) {
- if (builder3gppParams == null) {
- builder3gppParams = new Ike3gppParams.Builder();
+ // TODO(b/239753287): Telus carrier requests DEVICE_IDENTITY, but errors out when parsing
+ // the response. Temporarily disabled.
+ if (false) {
+ String imei = getMobileDeviceIdentity();
+ if (imei != null) {
+ if (builder3gppParams == null) {
+ builder3gppParams = new Ike3gppParams.Builder();
+ }
+ Log.d(TAG, "DEVICE_IDENTITY set in Ike3gppParams");
+ builder3gppParams.setMobileDeviceIdentity(imei);
}
- Log.d(TAG, "DEVICE_IDENTITY set in Ike3gppParams");
- builder3gppParams.setMobileDeviceIdentity(imei);
}
- if (setupRequest.pduSessionId() != 0) {
- if (builder3gppParams == null) {
+ 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());
}
- builder3gppParams.setPduSessionId((byte) setupRequest.pduSessionId());
}
if (builder3gppParams != null) {
Ike3gppExtension extension =
- new Ike3gppExtension(builder3gppParams.build(), new TmIke3gppCallback(apnName));
+ new Ike3gppExtension(
+ builder3gppParams.build(), new TmIke3gppCallback(apnName, token));
builder.setIke3gppExtension(extension);
}
int nattKeepAliveTimer =
- (int) getConfig(CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT);
+ getConfig(CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT);
if (nattKeepAliveTimer < NATT_KEEPALIVE_DELAY_SEC_MIN
|| nattKeepAliveTimer > NATT_KEEPALIVE_DELAY_SEC_MAX) {
Log.d(TAG, "Falling back to default natt keep alive timer");
nattKeepAliveTimer =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT);
}
builder.setNattKeepAliveDelaySeconds(nattKeepAliveTimer);
@@ -1027,23 +1045,17 @@ public class EpdgTunnelManager {
}
private boolean isValidChildSessionLifetime(int hardLifetimeSeconds, int softLifetimeSeconds) {
- if (hardLifetimeSeconds < CHILD_HARD_LIFETIME_SEC_MINIMUM
- || hardLifetimeSeconds > CHILD_HARD_LIFETIME_SEC_MAXIMUM
- || softLifetimeSeconds < CHILD_SOFT_LIFETIME_SEC_MINIMUM
- || hardLifetimeSeconds - softLifetimeSeconds < LIFETIME_MARGIN_SEC_MINIMUM) {
- return false;
- }
- return true;
+ return hardLifetimeSeconds >= CHILD_HARD_LIFETIME_SEC_MINIMUM
+ && hardLifetimeSeconds <= CHILD_HARD_LIFETIME_SEC_MAXIMUM
+ && softLifetimeSeconds >= CHILD_SOFT_LIFETIME_SEC_MINIMUM
+ && hardLifetimeSeconds - softLifetimeSeconds >= LIFETIME_MARGIN_SEC_MINIMUM;
}
private boolean isValidIkeSessionLifetime(int hardLifetimeSeconds, int softLifetimeSeconds) {
- if (hardLifetimeSeconds < IKE_HARD_LIFETIME_SEC_MINIMUM
- || hardLifetimeSeconds > IKE_HARD_LIFETIME_SEC_MAXIMUM
- || softLifetimeSeconds < IKE_SOFT_LIFETIME_SEC_MINIMUM
- || hardLifetimeSeconds - softLifetimeSeconds < LIFETIME_MARGIN_SEC_MINIMUM) {
- return false;
- }
- return true;
+ return hardLifetimeSeconds >= IKE_HARD_LIFETIME_SEC_MINIMUM
+ && hardLifetimeSeconds <= IKE_HARD_LIFETIME_SEC_MAXIMUM
+ && softLifetimeSeconds >= IKE_SOFT_LIFETIME_SEC_MINIMUM
+ && hardLifetimeSeconds - softLifetimeSeconds >= LIFETIME_MARGIN_SEC_MINIMUM;
}
private <T> T getConfig(String configKey) {
@@ -1242,74 +1254,122 @@ public class EpdgTunnelManager {
}
private void onSessionClosedWithException(
- IkeException exception, String apnName, int sessionType) {
- IwlanError error = new IwlanError(exception);
+ IkeException exception, String apnName, int token, int sessionType) {
Log.e(
TAG,
"Closing tunnel with exception for apn: "
+ apnName
+ + " with token: "
+ + token
+ " sessionType:"
- + sessionType
- + " error: "
- + error);
+ + sessionType);
exception.printStackTrace();
mHandler.sendMessage(
- mHandler.obtainMessage(sessionType, new SessionClosedData(apnName, error)));
+ mHandler.obtainMessage(
+ sessionType, new SessionClosedData(apnName, token, exception)));
+ }
+
+ private boolean isEpdgSelectionOrFirstTunnelBringUpInProgress() {
+ // Tunnel config is created but not connected to an ePDG. i.e., The first bring-up request
+ // in progress.
+ // No bring-up request in progress but pending queue is not empty. i.e. ePDG selection in
+ // progress
+ return (!mHasConnectedToEpdg && !mApnNameToTunnelConfig.isEmpty())
+ || !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) {
- Log.d(TAG, "msg.what = " + msg.what);
+ Log.d(TAG, "msg.what = " + eventToString(msg.what));
String apnName;
TunnelConfig tunnelConfig;
+ OnClosedMetrics.Builder onClosedMetricsBuilder;
+ switch (msg.what) {
+ case EVENT_CHILD_SESSION_OPENED:
+ case EVENT_IKE_SESSION_CLOSED:
+ case EVENT_IPSEC_TRANSFORM_CREATED:
+ case EVENT_IPSEC_TRANSFORM_DELETED:
+ case EVENT_CHILD_SESSION_CLOSED:
+ case EVENT_IKE_SESSION_OPENED:
+ case EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED:
+ case EVENT_IKE_3GPP_DATA_RECEIVED:
+ IkeEventData ikeEventData = (IkeEventData) msg.obj;
+ if (isObsoleteToken(ikeEventData.mApnName, ikeEventData.mToken)) {
+ Log.d(
+ TAG,
+ eventToString(msg.what)
+ + " for obsolete token "
+ + ikeEventData.mToken);
+ return;
+ }
+ }
+ long mIkeTunnelEstablishmentDuration;
switch (msg.what) {
case EVENT_TUNNEL_BRINGUP_REQUEST:
TunnelRequestWrapper tunnelRequestWrapper = (TunnelRequestWrapper) msg.obj;
TunnelSetupRequest setupRequest = tunnelRequestWrapper.getSetupRequest();
+ IwlanError bringUpError = null;
+
+ onClosedMetricsBuilder =
+ new OnClosedMetrics.Builder().setApnName(setupRequest.apnName());
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);
- 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());
return;
}
- // No tunnel bring up in progress and the epdg address is null
- if (!mIsEpdgAddressSelected
- && mApnNameToTunnelConfig.size() == 0
- && mRequestQueue.size() == 0) {
- mNetwork = setupRequest.network();
- mRequestQueue.add(tunnelRequestWrapper);
- selectEpdgAddress(setupRequest);
+ if (mHasConnectedToEpdg) {
+ // Service the request immediately when epdg address is available
+ onBringUpTunnel(
+ setupRequest,
+ tunnelRequestWrapper.getTunnelCallback(),
+ tunnelRequestWrapper.getTunnelMetrics());
break;
}
- // Service the request immediately when epdg address is available
- if (mIsEpdgAddressSelected) {
- onBringUpTunnel(setupRequest, tunnelRequestWrapper.getTunnelCallback());
- } else {
- mRequestQueue.add(tunnelRequestWrapper);
+ if (!isEpdgSelectionOrFirstTunnelBringUpInProgress()) {
+ // No tunnel bring-up in progress. Select the ePDG address first
+ selectEpdgAddress(setupRequest);
}
+
+ // Another bring-up or ePDG selection is in progress, pending this request.
+ mPendingBringUpRequests.add(tunnelRequestWrapper);
break;
case EVENT_EPDG_ADDRESS_SELECTION_REQUEST_COMPLETE:
@@ -1321,17 +1381,19 @@ public class EpdgTunnelManager {
break;
}
- if ((tunnelRequestWrapper = mRequestQueue.peek()) == null) {
+ if (mPendingBringUpRequests.isEmpty()) {
Log.d(TAG, "Empty request queue");
break;
}
if (selectorResult.getEpdgError().getErrorType() == IwlanError.NO_ERROR
&& selectorResult.getValidIpList() != null) {
+ tunnelRequestWrapper = mPendingBringUpRequests.remove();
validateAndSetEpdgAddress(selectorResult.getValidIpList());
onBringUpTunnel(
tunnelRequestWrapper.getSetupRequest(),
- tunnelRequestWrapper.getTunnelCallback());
+ tunnelRequestWrapper.getTunnelCallback(),
+ tunnelRequestWrapper.getTunnelMetrics());
} else {
IwlanError error =
(selectorResult.getEpdgError().getErrorType()
@@ -1372,11 +1434,28 @@ public class EpdgTunnelManager {
.build();
tunnelConfig.getTunnelCallback().onOpened(apnName, linkProperties);
- setIsEpdgAddressSelected(true);
+ reportIwlanError(apnName, new IwlanError(IwlanError.NO_ERROR));
+
+ mIkeTunnelEstablishmentDuration =
+ System.currentTimeMillis() - mIkeTunnelEstablishmentStartTime;
+ mIkeTunnelEstablishmentStartTime = 0;
+ tunnelConfig
+ .getTunnelMetrics()
+ .onOpened(
+ new OnOpenedMetrics.Builder()
+ .setApnName(apnName)
+ .setEpdgServerAddress(mEpdgAddress)
+ .setEpdgServerSelectionDuration(
+ (int) mEpdgServerSelectionDuration)
+ .setIkeTunnelEstablishmentDuration(
+ (int) mIkeTunnelEstablishmentDuration)
+ .build());
+
+ onConnectedToEpdg(true);
mValidEpdgInfo.resetIndex();
- mRequestQueue.poll();
printRequestQueue("EVENT_CHILD_SESSION_OPENED");
serviceAllPendingRequests();
+ tunnelConfig.setIkeSessionState(IkeSessionState.CHILD_SESSION_OPENED);
break;
case EVENT_IKE_SESSION_CLOSED:
@@ -1392,17 +1471,23 @@ public class EpdgTunnelManager {
// If IKE session closed exceptionally, we retrieve IwlanError directly from the
// exception; otherwise, it is still possible that we triggered an IKE session
- // close due to an error (eg. IwlanError.TUNNEL_TRANSFORM_FAILED), or because
+ // close due to an error (e.g. IwlanError.TUNNEL_TRANSFORM_FAILED), or because
// 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 setup failed without error cause, Iwlan reports
- // NETWORK_FAILURE instead of NO_ERROR
+ // If IKE session opened, then closed before child session (and IWLAN
+ // tunnel) opened.
+ // Iwlan reports IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED
+ // instead of NO_ERROR
if (!tunnelConfig.hasTunnelOpened()) {
- iwlanError = new IwlanError(IwlanError.NETWORK_FAILURE);
+ iwlanError = new IwlanError(
+ IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED);
} else {
iwlanError = tunnelConfig.getError();
}
@@ -1413,65 +1498,105 @@ public class EpdgTunnelManager {
iface.close();
}
- if (!mIsEpdgAddressSelected) {
- // fail all the requests. report back off timer, if present, to the
- // current request.
- if (tunnelConfig.isBackoffTimeValid()) {
- mRequestQueue.poll();
- reportIwlanError(apnName, iwlanError, tunnelConfig.getBackoffTime());
- tunnelConfig.getTunnelCallback().onClosed(apnName, iwlanError);
- }
- failAllPendingRequests(iwlanError);
- } else {
- mRequestQueue.poll();
- Log.d(TAG, "Tunnel Closed: " + iwlanError);
+ if (!tunnelConfig.hasTunnelOpened()) {
if (tunnelConfig.isBackoffTimeValid()) {
reportIwlanError(apnName, iwlanError, tunnelConfig.getBackoffTime());
} else {
reportIwlanError(apnName, iwlanError);
}
- tunnelConfig.getTunnelCallback().onClosed(apnName, iwlanError);
+ }
+
+ Log.d(TAG, "Tunnel Closed: " + iwlanError);
+ tunnelConfig.setIkeSessionState(IkeSessionState.NO_IKE_SESSION);
+ tunnelConfig.getTunnelCallback().onClosed(apnName, iwlanError);
+ onClosedMetricsBuilder = new OnClosedMetrics.Builder().setApnName(apnName);
+
+ if (!mHasConnectedToEpdg) {
+ failAllPendingRequests(iwlanError);
+ tunnelConfig.getTunnelMetrics().onClosed(onClosedMetricsBuilder.build());
+ } else {
+ mIkeTunnelEstablishmentDuration =
+ mIkeTunnelEstablishmentStartTime > 0
+ ? System.currentTimeMillis()
+ - mIkeTunnelEstablishmentStartTime
+ : 0;
+ mIkeTunnelEstablishmentStartTime = 0;
+
+ onClosedMetricsBuilder
+ .setEpdgServerAddress(mEpdgAddress)
+ .setEpdgServerSelectionDuration((int) mEpdgServerSelectionDuration)
+ .setIkeTunnelEstablishmentDuration(
+ (int) mIkeTunnelEstablishmentDuration);
+ tunnelConfig.getTunnelMetrics().onClosed(onClosedMetricsBuilder.build());
}
mApnNameToTunnelConfig.remove(apnName);
- if (mApnNameToTunnelConfig.size() == 0 && mRequestQueue.size() == 0) {
- resetTunnelManagerState();
+ if (mApnNameToTunnelConfig.size() == 0 && mPendingBringUpRequests.isEmpty()) {
+ onConnectedToEpdg(false);
}
+
break;
case EVENT_UPDATE_NETWORK:
UpdateNetworkWrapper updatedNetwork = (UpdateNetworkWrapper) msg.obj;
- apnName = updatedNetwork.getApnName();
- Network network = updatedNetwork.getNetwork();
- tunnelConfig = mApnNameToTunnelConfig.get(apnName);
-
- // 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);
+ mDefaultNetwork = updatedNetwork.getNetwork();
+ LinkProperties defaultLinkProperties = updatedNetwork.getLinkProperties();
+ String paraString = "Network: " + mDefaultNetwork;
+
+ 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;
case EVENT_TUNNEL_BRINGDOWN_REQUEST:
- apnName = (String) msg.obj;
- int forceClose = msg.arg1;
+ TunnelBringdownRequest bringdownRequest = (TunnelBringdownRequest) msg.obj;
+ apnName = bringdownRequest.mApnName;
+ boolean forceClose = bringdownRequest.mForceClose;
tunnelConfig = mApnNameToTunnelConfig.get(apnName);
if (tunnelConfig == null) {
- Log.d(
+ Log.w(
TAG,
"Bringdown request: No tunnel exists for apn: "
+ apnName
+ "forced: "
+ forceClose);
} else {
- if (forceClose == 1) {
+ if (forceClose) {
tunnelConfig.getIkeSession().kill();
} else {
tunnelConfig.getIkeSession().close();
@@ -1481,6 +1606,15 @@ public class EpdgTunnelManager {
if (numClosed > 0) {
Log.d(TAG, "Closed " + numClosed + " pending requests for apn: " + apnName);
}
+ if (tunnelConfig == null && numClosed == 0) {
+ // IwlanDataService expected to close a (pending or up) tunnel but was not
+ // found. Recovers state in IwlanDataService through TunnelCallback.
+ iwlanError = new IwlanError(IwlanError.TUNNEL_NOT_FOUND);
+ reportIwlanError(apnName, iwlanError);
+ bringdownRequest.mTunnelCallback.onClosed(apnName, iwlanError);
+ bringdownRequest.mIwlanTunnelMetrics.onClosed(
+ new OnClosedMetrics.Builder().setApnName(apnName).build());
+ }
break;
case EVENT_IPSEC_TRANSFORM_CREATED:
@@ -1490,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
- && mProtoFilter == EpdgSelector.PROTO_FILTER_IPV6) {
- 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(
@@ -1523,6 +1639,7 @@ public class EpdgTunnelManager {
}
try {
+ assert ipSecManager != null;
ipSecManager.applyTunnelModeTransform(
tunnelConfig.getIface(),
transformData.getDirection(),
@@ -1534,10 +1651,15 @@ public class EpdgTunnelManager {
closeIkeSession(
apnName, new IwlanError(IwlanError.TUNNEL_TRANSFORM_FAILED));
}
+ if (tunnelConfig.getIkeSessionState()
+ == IkeSessionState.IKE_MOBILITY_IN_PROGRESS) {
+ tunnelConfig.setIkeSessionState(IkeSessionState.CHILD_SESSION_OPENED);
+ }
break;
case EVENT_IPSEC_TRANSFORM_DELETED:
- IpSecTransform transform = (IpSecTransform) msg.obj;
+ transformData = (IpsecTransformData) msg.obj;
+ IpSecTransform transform = transformData.getTransform();
transform.close();
break;
@@ -1550,23 +1672,28 @@ 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;
case EVENT_IKE_SESSION_OPENED:
IkeSessionOpenedData ikeSessionOpenedData = (IkeSessionOpenedData) msg.obj;
+ apnName = ikeSessionOpenedData.mApnName;
IkeSessionConfiguration sessionConfiguration =
ikeSessionOpenedData.mIkeSessionConfiguration;
- tunnelConfig = mApnNameToTunnelConfig.get(ikeSessionOpenedData.mApnName);
+ tunnelConfig = mApnNameToTunnelConfig.get(apnName);
tunnelConfig.setPcscfAddrList(sessionConfiguration.getPcscfServers());
boolean enabledFastReauth =
- (boolean)
- getConfig(
- CarrierConfigManager.Iwlan
- .KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL);
+ getConfig(
+ CarrierConfigManager.Iwlan
+ .KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL);
Log.d(
TAG,
"CarrierConfigManager.Iwlan.KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL "
@@ -1574,7 +1701,7 @@ public class EpdgTunnelManager {
if (enabledFastReauth) {
EapInfo eapInfo = sessionConfiguration.getEapInfo();
- if (eapInfo != null && eapInfo instanceof EapAkaInfo) {
+ if (eapInfo instanceof EapAkaInfo) {
mNextReauthId = ((EapAkaInfo) eapInfo).getReauthId();
Log.d(TAG, "Update ReauthId: " + Arrays.toString(mNextReauthId));
} else {
@@ -1586,12 +1713,14 @@ 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 =
mContext.getSystemService(ConnectivityManager.class);
- if (connectivityManager.getLinkProperties(network) == null) {
+ if (Objects.requireNonNull(connectivityManager).getLinkProperties(network)
+ == null) {
Log.e(TAG, "Network " + network + " has null LinkProperties!");
return;
}
@@ -1610,6 +1739,38 @@ public class EpdgTunnelManager {
}
break;
+ case EVENT_IKE_3GPP_DATA_RECEIVED:
+ Ike3gppDataReceived ike3gppDataReceived = (Ike3gppDataReceived) msg.obj;
+ apnName = ike3gppDataReceived.mApnName;
+ List<Ike3gppData> ike3gppData = ike3gppDataReceived.mIke3gppData;
+ if (ike3gppData != null && !ike3gppData.isEmpty()) {
+ tunnelConfig = mApnNameToTunnelConfig.get(apnName);
+ for (Ike3gppData payload : ike3gppData) {
+ if (payload.getDataType() == DATA_TYPE_NOTIFY_N1_MODE_INFORMATION) {
+ Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_N1_MODE_INFORMATION");
+ NetworkSliceInfo si =
+ NetworkSliceSelectionAssistanceInformation.getSliceInfo(
+ ((Ike3gppN1ModeInformation) payload).getSnssai());
+ if (si != null) {
+ tunnelConfig.setSliceInfo(si);
+ Log.d(TAG, "SliceInfo: " + si);
+ }
+ } else if (payload.getDataType() == DATA_TYPE_NOTIFY_BACKOFF_TIMER) {
+ Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_BACKOFF_TIMER");
+ long backoffTime =
+ decodeBackoffTime(
+ ((Ike3gppBackoffTimer) payload).getBackoffTimer());
+ if (backoffTime > 0) {
+ tunnelConfig.setBackoffTime(backoffTime);
+ Log.d(TAG, "Backoff Timer: " + backoffTime);
+ }
+ }
+ }
+ } else {
+ Log.e(TAG, "Null or empty payloads received:");
+ }
+ break;
+
default:
throw new IllegalStateException("Unexpected value: " + msg.what);
}
@@ -1627,35 +1788,58 @@ public class EpdgTunnelManager {
}
private void selectEpdgAddress(TunnelSetupRequest setupRequest) {
- mLocalAddresses = getAddressForNetwork(mNetwork, mContext);
- if (mLocalAddresses == null || mLocalAddresses.size() == 0) {
- Log.e(TAG, "No local addresses available.");
- failAllPendingRequests(
- new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED));
+ ++mTransactionId;
+ mEpdgServerSelectionStartTime = System.currentTimeMillis();
+
+ final int ipPreference =
+ IwlanHelper.getConfig(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ mContext,
+ mSlotId);
+
+ IpPreferenceConflict ipPreferenceConflict =
+ isIpPreferenceConflictsWithNetwork(ipPreference);
+ if (ipPreferenceConflict.mIsConflict) {
+ sendSelectionRequestComplete(
+ null, new IwlanError(ipPreferenceConflict.mErrorType), mTransactionId);
return;
}
- mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
- if (!IwlanHelper.hasIpv6Address(mLocalAddresses)) {
- mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4;
- }
- if (!IwlanHelper.hasIpv4Address(mLocalAddresses)) {
- mProtoFilter = EpdgSelector.PROTO_FILTER_IPV6;
+ int protoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
+ int epdgAddressOrder = EpdgSelector.SYSTEM_PREFERRED;
+ switch (ipPreference) {
+ case CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_PREFERRED:
+ epdgAddressOrder = EpdgSelector.IPV4_PREFERRED;
+ break;
+ case CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_PREFERRED:
+ epdgAddressOrder = EpdgSelector.IPV6_PREFERRED;
+ break;
+ case CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_ONLY:
+ protoFilter = EpdgSelector.PROTO_FILTER_IPV4;
+ break;
+ case CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_ONLY:
+ protoFilter = EpdgSelector.PROTO_FILTER_IPV6;
+ break;
+ case CarrierConfigManager.Iwlan.EPDG_ADDRESS_SYSTEM_PREFERRED:
+ break;
+ default:
+ Log.w(TAG, "Invalid Ip preference : " + ipPreference);
}
EpdgSelector epdgSelector = getEpdgSelector();
IwlanError epdgError =
epdgSelector.getValidatedServerList(
- ++mTransactionId,
- mProtoFilter,
+ mTransactionId,
+ protoFilter,
+ epdgAddressOrder,
setupRequest.isRoaming(),
setupRequest.isEmergency(),
- mNetwork,
+ mDefaultNetwork,
mSelectorCallback);
if (epdgError.getErrorType() != IwlanError.NO_ERROR) {
Log.e(TAG, "Epdg address selection failed with error:" + epdgError);
- failAllPendingRequests(epdgError);
+ sendSelectionRequestComplete(null, epdgError, mTransactionId);
}
}
@@ -1667,24 +1851,29 @@ public class EpdgTunnelManager {
@VisibleForTesting
int closePendingRequestsForApn(String apnName) {
int numRequestsClosed = 0;
- int queueSize = mRequestQueue.size();
+ int queueSize = mPendingBringUpRequests.size();
if (queueSize == 0) {
return numRequestsClosed;
}
- int count = 0;
-
- while (count < queueSize) {
- TunnelRequestWrapper requestWrapper = mRequestQueue.poll();
- if (requestWrapper.getSetupRequest().apnName() == apnName) {
+ for (int count = 0; count < queueSize; count++) {
+ TunnelRequestWrapper requestWrapper = mPendingBringUpRequests.remove();
+ if (requestWrapper.getSetupRequest().apnName().equals(apnName)) {
requestWrapper
.getTunnelCallback()
.onClosed(apnName, new IwlanError(IwlanError.NO_ERROR));
+
+ requestWrapper
+ .getTunnelMetrics()
+ .onClosed(
+ new OnClosedMetrics.Builder()
+ .setApnName(apnName)
+ .setEpdgServerAddress(mEpdgAddress)
+ .build());
numRequestsClosed++;
} else {
- mRequestQueue.add(requestWrapper);
+ mPendingBringUpRequests.add(requestWrapper);
}
- count++;
}
return numRequestsClosed;
}
@@ -1709,69 +1898,74 @@ public class EpdgTunnelManager {
mValidEpdgInfo.incrementIndex();
}
- @VisibleForTesting
- void resetTunnelManagerState() {
- Log.d(TAG, "resetTunnelManagerState");
- mEpdgAddress = null;
- setIsEpdgAddressSelected(false);
- mNetwork = null;
- mRequestQueue = new LinkedList<>();
- mApnNameToTunnelConfig = new ConcurrentHashMap<>();
- mLocalAddresses = null;
- }
-
private void serviceAllPendingRequests() {
- while (mRequestQueue.size() > 0) {
+ while (!mPendingBringUpRequests.isEmpty()) {
Log.d(TAG, "serviceAllPendingRequests");
- TunnelRequestWrapper request = mRequestQueue.poll();
- onBringUpTunnel(request.getSetupRequest(), request.getTunnelCallback());
+ TunnelRequestWrapper request = mPendingBringUpRequests.remove();
+ onBringUpTunnel(
+ request.getSetupRequest(),
+ request.getTunnelCallback(),
+ request.getTunnelMetrics());
}
}
private void failAllPendingRequests(IwlanError error) {
- while (mRequestQueue.size() > 0) {
+ while (!mPendingBringUpRequests.isEmpty()) {
Log.d(TAG, "failAllPendingRequests");
- TunnelRequestWrapper request = mRequestQueue.poll();
+ TunnelRequestWrapper request = mPendingBringUpRequests.remove();
TunnelSetupRequest setupRequest = request.getSetupRequest();
reportIwlanError(setupRequest.apnName(), error);
request.getTunnelCallback().onClosed(setupRequest.apnName(), error);
+ request.getTunnelMetrics()
+ .onClosed(
+ new OnClosedMetrics.Builder()
+ .setApnName(setupRequest.apnName())
+ .setEpdgServerAddress(mEpdgAddress)
+ .build());
}
}
- // Prints mRequestQueue
+ // Prints mPendingBringUpRequests
private void printRequestQueue(String info) {
Log.d(TAG, info);
- Log.d(TAG, "mRequestQueue: " + Arrays.toString(mRequestQueue.toArray()));
+ Log.d(
+ TAG,
+ "mPendingBringUpRequests: " + Arrays.toString(mPendingBringUpRequests.toArray()));
}
// 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
private static final class TunnelRequestWrapper {
private final TunnelSetupRequest mSetupRequest;
private final TunnelCallback mTunnelCallback;
+ private final TunnelMetricsInterface mTunnelMetrics;
private TunnelRequestWrapper(
- TunnelSetupRequest setupRequest, TunnelCallback tunnelCallback) {
+ TunnelSetupRequest setupRequest,
+ TunnelCallback tunnelCallback,
+ TunnelMetricsInterface tunnelMetrics) {
mTunnelCallback = tunnelCallback;
mSetupRequest = setupRequest;
+ mTunnelMetrics = tunnelMetrics;
}
public TunnelSetupRequest getSetupRequest() {
@@ -1781,6 +1975,28 @@ public class EpdgTunnelManager {
public TunnelCallback getTunnelCallback() {
return mTunnelCallback;
}
+
+ public TunnelMetricsInterface getTunnelMetrics() {
+ return mTunnelMetrics;
+ }
+ }
+
+ private static final class TunnelBringdownRequest {
+ final String mApnName;
+ final boolean mForceClose;
+ final TunnelCallback mTunnelCallback;
+ final IwlanTunnelMetricsImpl mIwlanTunnelMetrics;
+
+ private TunnelBringdownRequest(
+ String apnName,
+ boolean forceClose,
+ TunnelCallback tunnelCallback,
+ IwlanTunnelMetricsImpl iwlanTunnelMetrics) {
+ mApnName = apnName;
+ mForceClose = forceClose;
+ mTunnelCallback = tunnelCallback;
+ mIwlanTunnelMetrics = iwlanTunnelMetrics;
+ }
}
private static final class EpdgSelectorResult {
@@ -1810,70 +2026,71 @@ public class EpdgTunnelManager {
}
// Data received from IkeSessionStateMachine on successful EVENT_CHILD_SESSION_OPENED.
- private static final class TunnelOpenedData {
- final String mApnName;
+ private static final class TunnelOpenedData extends IkeEventData {
final List<InetAddress> mInternalDnsServers;
final List<LinkAddress> mInternalAddresses;
private TunnelOpenedData(
String apnName,
+ int token,
List<InetAddress> internalDnsServers,
List<LinkAddress> internalAddresses) {
- mApnName = apnName;
+ super(apnName, token);
mInternalDnsServers = internalDnsServers;
mInternalAddresses = internalAddresses;
}
}
// Data received from IkeSessionStateMachine on successful EVENT_IKE_SESSION_OPENED.
- private static final class IkeSessionOpenedData {
- final String mApnName;
+ private static final class IkeSessionOpenedData extends IkeEventData {
final IkeSessionConfiguration mIkeSessionConfiguration;
private IkeSessionOpenedData(
- String apnName, IkeSessionConfiguration ikeSessionConfiguration) {
- mApnName = apnName;
+ String apnName, int token, IkeSessionConfiguration ikeSessionConfiguration) {
+ super(apnName, token);
mIkeSessionConfiguration = ikeSessionConfiguration;
}
}
- private static final class IkeSessionConnectionInfoData {
- final String mApnName;
+ private static final class IkeSessionConnectionInfoData extends IkeEventData {
final IkeSessionConnectionInfo mIkeSessionConnectionInfo;
private IkeSessionConnectionInfoData(
- String apnName, IkeSessionConnectionInfo ikeSessionConnectionInfo) {
- mApnName = apnName;
+ String apnName, int token, IkeSessionConnectionInfo ikeSessionConnectionInfo) {
+ super(apnName, token);
mIkeSessionConnectionInfo = ikeSessionConnectionInfo;
}
}
- // Data received from IkeSessionStateMachine if either IKE session or Child session have been
- // closed, normally or exceptionally.
- private static final class SessionClosedData {
- final String mApnName;
- final IwlanError mIwlanError;
+ private static final class Ike3gppDataReceived extends IkeEventData {
+ final List<Ike3gppData> mIke3gppData;
- private SessionClosedData(String apnName, IwlanError iwlanError) {
- mApnName = apnName;
- mIwlanError = iwlanError;
+ private Ike3gppDataReceived(String apnName, int token, List<Ike3gppData> ike3gppData) {
+ super(apnName, token);
+ mIke3gppData = ike3gppData;
}
}
- public void releaseInstance() {
- mHandlerThread.quit();
- mTunnelManagerInstances.remove(mSlotId);
+ // 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 IkeException mIkeException;
+
+ private SessionClosedData(String apnName, int token, IkeException ikeException) {
+ super(apnName, token);
+ mIkeException = ikeException;
+ }
}
- private static final class IpsecTransformData {
+ private static final class IpsecTransformData extends IkeEventData {
private final IpSecTransform mTransform;
private final int mDirection;
- private final String mApnName;
- private IpsecTransformData(IpSecTransform transform, int direction, String apnName) {
+ private IpsecTransformData(
+ IpSecTransform transform, int direction, String apnName, int token) {
+ super(apnName, token);
mTransform = transform;
mDirection = direction;
- mApnName = apnName;
}
public IpSecTransform getTransform() {
@@ -1885,7 +2102,17 @@ public class EpdgTunnelManager {
}
public String getApnName() {
- return mApnName;
+ return super.mApnName;
+ }
+ }
+
+ private abstract static class IkeEventData {
+ final String mApnName;
+ final int mToken;
+
+ private IkeEventData(String apnName, int token) {
+ mApnName = apnName;
+ mToken = token;
}
}
@@ -1924,16 +2151,28 @@ public class EpdgTunnelManager {
}
}
+ private static class IpPreferenceConflict {
+ final boolean mIsConflict;
+ final int mErrorType;
+
+ private IpPreferenceConflict(boolean isConflict, int errorType) {
+ mIsConflict = isConflict;
+ mErrorType = errorType;
+ }
+
+ private IpPreferenceConflict() {
+ mIsConflict = false;
+ mErrorType = IwlanError.NO_ERROR;
+ }
+ }
+
private int[] getRetransmissionTimeoutsFromConfig() {
- int[] timeList =
- (int[]) getConfig(CarrierConfigManager.Iwlan.KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY);
- boolean isValid = true;
- if (timeList == null
- || timeList.length == 0
- || timeList.length > IKE_RETRANS_MAX_ATTEMPTS_MAX) {
- isValid = false;
- }
- for (int time : timeList) {
+ int[] timeList = getConfig(CarrierConfigManager.Iwlan.KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY);
+ boolean isValid =
+ timeList != null
+ && timeList.length != 0
+ && timeList.length <= IKE_RETRANS_MAX_ATTEMPTS_MAX;
+ for (int time : Objects.requireNonNull(timeList)) {
if (time < IKE_RETRANS_TIMEOUT_MS_MIN || time > IKE_RETRANS_TIMEOUT_MS_MAX) {
isValid = false;
break;
@@ -1941,21 +2180,18 @@ public class EpdgTunnelManager {
}
if (!isValid) {
timeList =
- (int[])
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan.KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY);
+ IwlanHelper.getDefaultConfig(
+ CarrierConfigManager.Iwlan.KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY);
}
Log.d(TAG, "getRetransmissionTimeoutsFromConfig: " + Arrays.toString(timeList));
return timeList;
}
private int getDpdDelayFromConfig() {
- int dpdDelay = (int) getConfig(CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT);
+ int dpdDelay = getConfig(CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT);
if (dpdDelay < IKE_DPD_DELAY_SEC_MIN || dpdDelay > IKE_DPD_DELAY_SEC_MAX) {
dpdDelay =
- (int)
- IwlanHelper.getDefaultConfig(
- CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT);
+ IwlanHelper.getDefaultConfig(CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT);
}
return dpdDelay;
}
@@ -1971,7 +2207,7 @@ public class EpdgTunnelManager {
* incremented in multiples of 1 minute 1 1 0 value is incremented in multiples of 1 hour 1 1 1
* value indicates that the timer is deactivated.
*
- * @param backoffTimerByte Byte value obtained from ike
+ * @param backoffTimeByte Byte value obtained from ike
* @return long time value in seconds. -1 if the timer needs to be deactivated.
*/
private static long decodeBackoffTime(byte backoffTimeByte) {
@@ -1979,12 +2215,12 @@ public class EpdgTunnelManager {
final int BACKOFF_TIMER_UNIT_MASK = 0xE0;
final Long[] BACKOFF_TIMER_UNIT_INCREMENT_SECS = {
10L * 60L, // 10 mins
- 1L * 60L * 60L, // 1 hour
+ 60L * 60L, // 1 hour
10L * 60L * 60L, // 10 hours
2L, // 2 seconds
30L, // 30 seconds
- 1L * 60L, // 1 minute
- 1L * 60L * 60L, // 1 hour
+ 60L, // 1 minute
+ 60L * 60L, // 1 hour
};
long time = backoffTimeByte & BACKOFF_TIME_VALUE_MASK;
@@ -1998,8 +2234,7 @@ public class EpdgTunnelManager {
@VisibleForTesting
String getTunnelSetupRequestApnName(TunnelSetupRequest setupRequest) {
- String apnName = setupRequest.apnName();
- return apnName;
+ return setupRequest.apnName();
}
@VisibleForTesting
@@ -2007,29 +2242,48 @@ public class EpdgTunnelManager {
String apnName,
IkeSession ikeSession,
TunnelCallback tunnelCallback,
+ TunnelMetricsInterface tunnelMetrics,
InetAddress srcIpv6Addr,
int srcIPv6AddrPrefixLen) {
mApnNameToTunnelConfig.put(
apnName,
- new TunnelConfig(ikeSession, tunnelCallback, srcIpv6Addr, srcIPv6AddrPrefixLen));
+ new TunnelConfig(
+ ikeSession,
+ tunnelCallback,
+ tunnelMetrics,
+ srcIpv6Addr,
+ srcIPv6AddrPrefixLen));
Log.d(TAG, "Added apn: " + apnName + " to TunnelConfig");
}
@VisibleForTesting
- boolean isTunnelConfigContainExistApn(String apnName) {
- boolean ret = mApnNameToTunnelConfig.containsKey(apnName);
- return ret;
+ int incrementAndGetCurrentTokenForApn(String apnName) {
+ final int currentToken =
+ mApnNameToCurrentToken.compute(
+ apnName, (apn, token) -> token == null ? 0 : token + 1);
+ Log.d(TAG, "Added token: " + currentToken + " for apn: " + apnName);
+ return currentToken;
}
@VisibleForTesting
- List<InetAddress> getAddressForNetwork(Network network, Context context) {
- List<InetAddress> ret = IwlanHelper.getAddressesForNetwork(network, context);
- return ret;
+ 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
- EpdgSelector.EpdgSelectorCallback getSelectorCallback() {
- return mSelectorCallback;
+ boolean isTunnelConfigContainExistApn(String apnName) {
+ return mApnNameToTunnelConfig.containsKey(apnName);
+ }
+
+ @VisibleForTesting
+ List<InetAddress> getAddressForNetwork(Network network, Context context) {
+ return IwlanHelper.getAllAddressesForNetwork(network, context);
}
@VisibleForTesting
@@ -2039,7 +2293,9 @@ 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 =
new EpdgSelectorResult(validIPList, result, transactionId);
mHandler.sendMessage(
@@ -2053,14 +2309,58 @@ public class EpdgTunnelManager {
|| proto == ApnSetting.PROTOCOL_IPV6);
}
+ boolean isObsoleteToken(String apnName, int token) {
+ if (!mApnNameToCurrentToken.containsKey(apnName)) {
+ return true;
+ }
+ return token != mApnNameToCurrentToken.get(apnName);
+ }
+
+ private static String eventToString(int event) {
+ switch (event) {
+ case EVENT_TUNNEL_BRINGUP_REQUEST:
+ return "EVENT_TUNNEL_BRINGUP_REQUEST";
+ case EVENT_TUNNEL_BRINGDOWN_REQUEST:
+ return "EVENT_TUNNEL_BRINGDOWN_REQUEST";
+ case EVENT_CHILD_SESSION_OPENED:
+ return "EVENT_CHILD_SESSION_OPENED";
+ case EVENT_CHILD_SESSION_CLOSED:
+ return "EVENT_CHILD_SESSION_CLOSED";
+ case EVENT_IKE_SESSION_CLOSED:
+ return "EVENT_IKE_SESSION_CLOSED";
+ case EVENT_EPDG_ADDRESS_SELECTION_REQUEST_COMPLETE:
+ return "EVENT_EPDG_ADDRESS_SELECTION_REQUEST_COMPLETE";
+ case EVENT_IPSEC_TRANSFORM_CREATED:
+ return "EVENT_IPSEC_TRANSFORM_CREATED";
+ case EVENT_IPSEC_TRANSFORM_DELETED:
+ return "EVENT_IPSEC_TRANSFORM_DELETED";
+ case EVENT_UPDATE_NETWORK:
+ return "EVENT_UPDATE_NETWORK";
+ case EVENT_IKE_SESSION_OPENED:
+ return "EVENT_IKE_SESSION_OPENED";
+ case EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED:
+ return "EVENT_IKE_SESSION_CONNECTION_INFO_CHANGED";
+ case EVENT_IKE_3GPP_DATA_RECEIVED:
+ return "EVENT_IKE_3GPP_DATA_RECEIVED";
+ default:
+ return "Unknown(" + event + ")";
+ }
+ }
+
@VisibleForTesting
- TmIkeSessionCallback getTmIkeSessionCallback(String apnName) {
- return new TmIkeSessionCallback(apnName);
+ TmIkeSessionCallback getTmIkeSessionCallback(String apnName, int token) {
+ return new TmIkeSessionCallback(apnName, token);
}
@VisibleForTesting
- void setIsEpdgAddressSelected(boolean value) {
- mIsEpdgAddressSelected = value;
+ void onConnectedToEpdg(boolean hasConnected) {
+ mHasConnectedToEpdg = hasConnected;
+ if (mHasConnectedToEpdg) {
+ mIkeSessionNetwork = mDefaultNetwork;
+ } else {
+ mIkeSessionNetwork = null;
+ mEpdgAddress = null;
+ }
}
@VisibleForTesting
@@ -2069,6 +2369,14 @@ public class EpdgTunnelManager {
}
@VisibleForTesting
+ int getCurrentTokenForApn(String apnName) {
+ if (!mApnNameToCurrentToken.containsKey(apnName)) {
+ throw new IllegalArgumentException("There is no token for apn: " + apnName);
+ }
+ return mApnNameToCurrentToken.get(apnName);
+ }
+
+ @VisibleForTesting
long reportIwlanError(String apnName, IwlanError error) {
return ErrorPolicyManager.getInstance(mContext, mSlotId).reportIwlanError(apnName, error);
}
@@ -2089,9 +2397,49 @@ public class EpdgTunnelManager {
return ErrorPolicyManager.getInstance(mContext, mSlotId).canBringUpTunnel(apnName);
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ @VisibleForTesting
+ void setEpdgAddress(InetAddress inetAddress) {
+ mEpdgAddress = inetAddress;
+ }
+
+ @VisibleForTesting
+ IpPreferenceConflict isIpPreferenceConflictsWithNetwork(
+ @CarrierConfigManager.Iwlan.EpdgAddressIpPreference int ipPreference) {
+ 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(localAddresses)
+ && ipPreference == CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_ONLY) {
+ Log.e(
+ TAG,
+ "ePDG IP preference: "
+ + ipPreference
+ + " conflicts with source IP type: "
+ + EpdgSelector.PROTO_FILTER_IPV4);
+ return new IpPreferenceConflict(true, IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED);
+ } 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
+ // this IPv4 address, UE will connect to IPv4 ePDG server through XLAT. However, there
+ // are issues on connecting ePDG server through XLAT. Will allow IPV4_ONLY on IPv6 WiFi
+ // after the issues are resolved.
+ Log.e(
+ TAG,
+ "ePDG IP preference: "
+ + ipPreference
+ + " conflicts with source IP type: "
+ + EpdgSelector.PROTO_FILTER_IPV6);
+ return new IpPreferenceConflict(true, IwlanError.EPDG_ADDRESS_ONLY_IPV4_ALLOWED);
+ }
+ return new IpPreferenceConflict();
+ }
+
+ public void dump(PrintWriter pw) {
pw.println("---- EpdgTunnelManager ----");
- pw.println("mIsEpdgAddressSelected: " + mIsEpdgAddressSelected);
+ pw.println("mHasConnectedToEpdg: " + mHasConnectedToEpdg);
+ pw.println("mIkeSessionNetwork: " + mIkeSessionNetwork);
if (mEpdgAddress != null) {
pw.println("mEpdgAddress: " + mEpdgAddress);
}
diff --git a/src/com/google/android/iwlan/epdg/IkeSessionState.java b/src/com/google/android/iwlan/epdg/IkeSessionState.java
new file mode 100644
index 0000000..ad3d5e3
--- /dev/null
+++ b/src/com/google/android/iwlan/epdg/IkeSessionState.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.iwlan.epdg;
+
+import com.google.android.iwlan.IwlanError;
+
+/** A state machine that infers the current IkeSession state. */
+enum IkeSessionState {
+ NO_IKE_SESSION {
+ @Override
+ public int getErrorType() {
+ return IwlanError.NO_ERROR;
+ }
+ },
+ IKE_SESSION_INIT_IN_PROGRESS {
+ @Override
+ public int getErrorType() {
+ return IwlanError.IKE_INIT_TIMEOUT;
+ }
+ },
+ IKE_MOBILITY_IN_PROGRESS {
+ @Override
+ public int getErrorType() {
+ return IwlanError.IKE_MOBILITY_TIMEOUT;
+ }
+ },
+ CHILD_SESSION_OPENED {
+ @Override
+ public int getErrorType() {
+ return IwlanError.IKE_DPD_TIMEOUT;
+ }
+ };
+
+ /**
+ * Called when IkeSession report error with IkeIOException, check current IkeSession state and
+ * return corresponding time out error.
+ *
+ * @return NO_ERROR or IWLAN IKE time out error
+ */
+ public abstract int getErrorType();
+}
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
new file mode 100644
index 0000000..7ecf464
--- /dev/null
+++ b/src/com/google/android/iwlan/proto/MetricsAtom.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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 {
+ private int mMessageId;
+ private int mApnType;
+ private boolean mIsHandover;
+ private String mEpdgServerAddress;
+ private int mSourceRat;
+ private boolean mIsCellularRoaming;
+ private boolean mIsNetworkConnected;
+ private int mTransportType;
+ private int mSetupRequestResult;
+ private int mIwlanError;
+ private int mDataCallFailCause;
+ private int mProcessingDurationMillis;
+ private int mEpdgServerSelectionDurationMillis;
+ private int mIkeTunnelEstablishmentDurationMillis;
+ private int mTunnelState;
+ private int mHandoverFailureMode;
+ private int mRetryDurationMillis;
+ private int mWifiSignalValue;
+ private String mIwlanErrorWrappedClassname;
+ private String mIwlanErrorWrappedStackFirstFrame;
+
+ public void setMessageId(int messageId) {
+ this.mMessageId = messageId;
+ }
+
+ public void setApnType(int apnType) {
+ this.mApnType = apnType;
+ }
+
+ public void setIsHandover(boolean isHandover) {
+ this.mIsHandover = isHandover;
+ }
+
+ public void setEpdgServerAddress(String epdgServerAddress) {
+ this.mEpdgServerAddress = epdgServerAddress;
+ }
+
+ public void setSourceRat(int sourceRat) {
+ this.mSourceRat = sourceRat;
+ }
+
+ public void setIsCellularRoaming(boolean isCellularRoaming) {
+ this.mIsCellularRoaming = isCellularRoaming;
+ }
+
+ public void setIsNetworkConnected(boolean isNetworkConnected) {
+ this.mIsNetworkConnected = isNetworkConnected;
+ }
+
+ public void setTransportType(int transportType) {
+ this.mTransportType = transportType;
+ }
+
+ public void setSetupRequestResult(int setupRequestResult) {
+ this.mSetupRequestResult = setupRequestResult;
+ }
+
+ public void setIwlanError(int iwlanError) {
+ this.mIwlanError = iwlanError;
+ }
+
+ public void setDataCallFailCause(int dataCallFailCause) {
+ this.mDataCallFailCause = dataCallFailCause;
+ }
+
+ public void setProcessingDurationMillis(int processingDurationMillis) {
+ this.mProcessingDurationMillis = processingDurationMillis;
+ }
+
+ public void setEpdgServerSelectionDurationMillis(int epdgServerSelectionDurationMillis) {
+ this.mEpdgServerSelectionDurationMillis = epdgServerSelectionDurationMillis;
+ }
+
+ public void setIkeTunnelEstablishmentDurationMillis(int ikeTunnelEstablishmentDurationMillis) {
+ this.mIkeTunnelEstablishmentDurationMillis = ikeTunnelEstablishmentDurationMillis;
+ }
+
+ public void setTunnelState(int tunnelState) {
+ this.mTunnelState = tunnelState;
+ }
+
+ public void setHandoverFailureMode(int handoverFailureMode) {
+ this.mHandoverFailureMode = handoverFailureMode;
+ }
+
+ public void setRetryDurationMillis(int retryDurationMillis) {
+ this.mRetryDurationMillis = retryDurationMillis;
+ }
+
+ public void setWifiSignalValue(int wifiSignalValue) {
+ 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(
+ mMessageId,
+ mApnType,
+ mIsHandover,
+ mEpdgServerAddress,
+ mSourceRat,
+ mIsCellularRoaming,
+ mIsNetworkConnected,
+ mTransportType,
+ mSetupRequestResult,
+ mIwlanError,
+ mDataCallFailCause,
+ mProcessingDurationMillis,
+ mEpdgServerSelectionDurationMillis,
+ mIkeTunnelEstablishmentDurationMillis,
+ mTunnelState,
+ mHandoverFailureMode,
+ mRetryDurationMillis,
+ mIwlanErrorWrappedClassname,
+ mIwlanErrorWrappedStackFirstFrame);
+ return;
+ } else if (mMessageId == IwlanStatsLog.IWLAN_PDN_DISCONNECTED_REASON_REPORTED) {
+ IwlanStatsLog.write(
+ mMessageId,
+ mDataCallFailCause,
+ mIsNetworkConnected,
+ mTransportType,
+ mWifiSignalValue);
+ return;
+ }
+ }
+}
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index 3bc624b..1c15e41 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
@@ -30,6 +31,7 @@ import android.content.Context;
import android.content.res.AssetManager;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.PersistableBundle;
+import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
@@ -39,6 +41,9 @@ import android.telephony.data.DataService;
import androidx.test.InstrumentationRegistry;
+import com.google.auto.value.AutoValue;
+
+import org.json.JSONArray;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,9 +53,80 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.io.InputStream;
+import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class ErrorPolicyManagerTest {
+ @AutoValue
+ abstract static class ErrorPolicyString {
+ abstract String errorType();
+
+ abstract List<String> errorDetails();
+
+ abstract List<String> retryArray();
+
+ abstract List<String> unthrottlingEvents();
+
+ abstract Optional<String> numAttemptsPerFqdn();
+
+ abstract Optional<String> handoverAttemptCount();
+
+ static Builder builder() {
+ return new AutoValue_ErrorPolicyManagerTest_ErrorPolicyString.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setErrorType(String errorType);
+
+ abstract Builder setErrorDetails(List<String> errorDetails);
+
+ abstract Builder setRetryArray(List<String> retryArray);
+
+ abstract Builder setUnthrottlingEvents(List<String> unthrottlingEvents);
+
+ abstract Builder setNumAttemptsPerFqdn(String numAttemptsPerFqdn);
+
+ abstract Builder setHandoverAttemptCount(String handoverAttemptCount);
+
+ abstract ErrorPolicyString build();
+ }
+
+ String getErrorPolicyInString() {
+ StringBuilder errorPolicy =
+ new StringBuilder(
+ "\"ErrorType\": \""
+ + errorType()
+ + "\","
+ + "\"ErrorDetails\": [\""
+ + String.join("\", \"", errorDetails())
+ + "\"],"
+ + "\"RetryArray\": [\""
+ + String.join("\", \"", retryArray())
+ + "\"],"
+ + "\"UnthrottlingEvents\": [\""
+ + String.join("\", \"", unthrottlingEvents())
+ + "\"]");
+
+ numAttemptsPerFqdn()
+ .ifPresent(
+ numAttemptsPerFqdn ->
+ errorPolicy
+ .append(",\"NumAttemptsPerFqdn\": \"")
+ .append(numAttemptsPerFqdn)
+ .append("\""));
+ handoverAttemptCount()
+ .ifPresent(
+ handoverAttemptCount ->
+ errorPolicy
+ .append(",\"HandoverAttemptCount\": \"")
+ .append(handoverAttemptCount)
+ .append("\""));
+ return errorPolicy.toString();
+ }
+ }
+
private static final String TAG = "ErrorPolicyManagerTest";
// @Rule public final MockitoRule mockito = MockitoJUnit.rule();
@@ -60,6 +136,9 @@ public class ErrorPolicyManagerTest {
private static final int DEFAULT_SUBID = 0;
private static final int TEST_CARRIER_ID = 1;
+ private TestLooper mTestLooper = new TestLooper();
+ private long mMockedClockTime = 0;
+
@Mock private Context mMockContext;
@Mock CarrierConfigManager mMockCarrierConfigManager;
@Mock SubscriptionManager mMockSubscriptionManager;
@@ -75,17 +154,22 @@ public class ErrorPolicyManagerTest {
mStaticMockSession =
mockitoSession()
.mockStatic(IwlanDataService.class)
+ .spyStatic(IwlanHelper.class)
.strictness(Strictness.LENIENT)
.startMocking();
when(IwlanDataService.getDataServiceProvider(anyInt()))
.thenReturn(mMockDataServiceProvider);
+ when(IwlanHelper.elapsedRealtime()).thenAnswer(i -> mMockedClockTime);
AssetManager mockAssetManager = mock(AssetManager.class);
Context context = InstrumentationRegistry.getTargetContext();
InputStream is = context.getResources().getAssets().open("defaultiwlanerrorconfig.json");
doReturn(mockAssetManager).when(mMockContext).getAssets();
doReturn(is).when(mockAssetManager).open(any());
setupMockForCarrierConfig(null);
+ ErrorPolicyManager.resetAllInstances();
mErrorPolicyManager = spy(ErrorPolicyManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX));
+ doReturn(mTestLooper.getLooper()).when(mErrorPolicyManager).getLooper();
+ mErrorPolicyManager.initHandler();
}
@After
@@ -113,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";
@@ -122,17 +210,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34", "9000-9050"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34", "9000-9050"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -143,8 +236,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -187,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
@@ -199,11 +291,14 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"WRONG_ERROR_DETAIL"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("WRONG_ERROR_DETAIL"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -214,13 +309,16 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// Fallback to default Iwlan error policy for IKE_PROTOCOL_ERROR_TYPE(24) because of failed
// 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);
@@ -228,10 +326,30 @@ public class ErrorPolicyManagerTest {
assertEquals(40, time);
time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
assertEquals(80, time);
+
+ iwlanError = buildIwlanIkeProtocolError(9002);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(5, time);
time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
- assertEquals(160, time);
+ assertEquals(10, time);
time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
- assertEquals(86400, time);
+ assertEquals(10, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(20, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(40, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(80, time);
+
+ iwlanError = buildIwlanIkeInternalAddressFailure();
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(10, time);
}
@Test
@@ -243,17 +361,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"*"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("*"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -263,8 +386,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
mErrorPolicyManager.logErrorPolicies();
@@ -297,17 +419,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -317,8 +444,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -328,7 +454,7 @@ public class ErrorPolicyManagerTest {
boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
assertFalse(bringUpTunnel);
- sleep(4000);
+ advanceClockByTimeMs(4000);
bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
assertTrue(bringUpTunnel);
@@ -349,17 +475,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -369,8 +500,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -396,17 +526,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"6", "12", "24"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("6", "12", "24"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -416,7 +551,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 6, 12, 24
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -427,7 +562,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.WIFI_DISABLE_EVENT)
.sendToTarget();
- sleep(500);
+ advanceClockByTimeMs(500);
verify(mMockDataServiceProvider, times(1)).notifyApnUnthrottled(eq(apn));
boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
@@ -447,17 +582,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"6", "12", "24"},
- new String[] {"WIFI_CALLING_DISABLE_EVENT", "WIFI_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("6", "12", "24"))
+ .setUnthrottlingEvents(
+ List.of("WIFI_CALLING_DISABLE_EVENT", "WIFI_DISABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -467,7 +607,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 6, 12, 24
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -478,7 +618,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT)
.sendToTarget();
- sleep(500);
+ advanceClockByTimeMs(500);
verify(mMockDataServiceProvider, times(1)).notifyApnUnthrottled(eq(apn));
boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
@@ -498,17 +638,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_DISABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -518,7 +663,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -529,7 +674,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.APM_ENABLE_EVENT)
.sendToTarget();
- sleep(500);
+ advanceClockByTimeMs(500);
verify(mMockDataServiceProvider, times(1)).notifyApnUnthrottled(eq(apn));
boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
@@ -550,17 +695,22 @@ public class ErrorPolicyManagerTest {
+ apn1
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"4", "8", "16"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
PersistableBundle bundle = new PersistableBundle();
@@ -570,8 +720,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -607,17 +756,22 @@ public class ErrorPolicyManagerTest {
+ apn
+ "\","
+ "\"ErrorTypes\": [{"
- + getErrorTypeInJSON(
- "IKE_PROTOCOL_ERROR_TYPE",
- new String[] {"24", "34"},
- new String[] {"10", "15", "20"},
- new String[] {"APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("10", "15", "20"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}, {"
- + getErrorTypeInJSON(
- "GENERIC_ERROR_TYPE",
- new String[] {"SERVER_SELECTION_FAILED"},
- new String[] {"0"},
- new String[] {"APM_ENABLE_EVENT"})
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ "}]"
+ "}]";
@@ -628,8 +782,7 @@ public class ErrorPolicyManagerTest {
.mHandler
.obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
.sendToTarget();
-
- sleep(1000);
+ mTestLooper.dispatchAll();
// IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
IwlanError iwlanError = buildIwlanIkeAuthFailedError();
@@ -638,9 +791,9 @@ public class ErrorPolicyManagerTest {
time = Math.round((double) mErrorPolicyManager.getCurrentRetryTimeMs(apn) / 1000);
assertEquals(time, 2);
- // sleep for 2 seconds and make sure that we can bring up tunnel after 2 secs
+ // advanceClockByTimeMs for 2 seconds and make sure that we can bring up tunnel after 2 secs
// as back off time - 2 secs should override the retry time in policy - 10 secs
- sleep(2000);
+ advanceClockByTimeMs(2000);
boolean bringUpTunnel = mErrorPolicyManager.canBringUpTunnel(apn);
assertTrue(bringUpTunnel);
@@ -661,6 +814,207 @@ public class ErrorPolicyManagerTest {
}
@Test
+ public void testErrorPolicyWithNumAttemptsPerFqdn() throws Exception {
+ // ErrorPolicyManager#getCurrentFqdnIndex() is tested when the ErrorType
+ // parameter "NumAttemptsPerFqdn" is configured.
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("15500")) /* CONGESTION */
+ .setRetryArray(
+ List.of(
+ "0", "0", "300", "600", "1200", "0", "0", "0",
+ "300", "600", "1200", "-1"))
+ .setUnthrottlingEvents(
+ List.of(
+ "APM_ENABLE_EVENT",
+ "WIFI_DISABLE_EVENT",
+ "WIFI_CALLING_DISABLE_EVENT"))
+ .setNumAttemptsPerFqdn("6")
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+ assertEquals(DataFailCause.NONE, mErrorPolicyManager.getMostRecentDataFailCause());
+
+ // IKE_PROTOCOL_ERROR_TYPE(15500)
+ // UE constructs 2 PLMN FQDNs.
+ IwlanError iwlanError = buildIwlanIkeProtocolError(15500 /* CONGESTION */);
+
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ assertEquals(
+ DataFailCause.IWLAN_CONGESTION, mErrorPolicyManager.getMostRecentDataFailCause());
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(300, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(600, time);
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(1200, time);
+ assertEquals(0, mErrorPolicyManager.getCurrentFqdnIndex(2));
+
+ // Cycles to next FQDN
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ assertEquals(1, mErrorPolicyManager.getCurrentFqdnIndex(2));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ assertEquals(1, mErrorPolicyManager.getCurrentFqdnIndex(2));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(300, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(600, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(1200, time);
+
+ // Steady state retry duration, cycles back to 1st FQDN.
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(1200, time);
+ assertEquals(0, mErrorPolicyManager.getCurrentFqdnIndex(2));
+ }
+
+ @Test
+ public void testShouldRetryWithInitialAttach() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ // IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 4,8,16
+ IwlanError iwlanError = buildIwlanIkeAuthFailedError();
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(4, time);
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(8, time);
+ // Reached handover attempt count and error is IKE protocol error
+ assertTrue(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+ }
+
+ @Test
+ public void testShouldRetryWithInitialAttachForInternalError() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("IKE_PROTOCOL_ERROR_TYPE")
+ .setErrorDetails(List.of("24", "34"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}, {"
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("SERVER_SELECTION_FAILED"))
+ .setRetryArray(List.of("0", "0"))
+ .setUnthrottlingEvents(List.of("APM_ENABLE_EVENT"))
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+ mTestLooper.dispatchAll();
+
+ // GENERIC_PROTOCOL_ERROR_TYPE - SERVER_SELECTION_FAILED and retryArray = 0, 0
+ IwlanError iwlanError = new IwlanError(IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED);
+ long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(0, time);
+ // Should not retry with initial attach as the errors are not IKE_PROTOCOL_ERROR_TYPE
+ assertFalse(mErrorPolicyManager.shouldRetryWithInitialAttach(apn));
+ }
+
+ @Test
+ public void testHandoverAttemptCountInvalidErrorType() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + ErrorPolicyString.builder()
+ .setErrorType("GENERIC_ERROR_TYPE")
+ .setErrorDetails(List.of("*"))
+ .setRetryArray(List.of("4", "8", "16"))
+ .setUnthrottlingEvents(
+ List.of("APM_ENABLE_EVENT", "WIFI_AP_CHANGED_EVENT"))
+ .setHandoverAttemptCount("2")
+ .build()
+ .getErrorPolicyInString()
+ + "}]"
+ + "}]";
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mErrorPolicyManager.readErrorPolicies(new JSONArray(config)));
+ }
+
+ @Test
public void testErrorStats() throws Exception {
String apn1 = "ims";
String apn2 = "mms";
@@ -701,31 +1055,9 @@ public class ErrorPolicyManagerTest {
assertEquals(resultServerApn2, serverSelectionCountApn2);
}
- private String getErrorTypeInJSON(
- String ErrorType,
- String[] errorDetails,
- String[] retryArray,
- String[] unthrottlingEvents) {
- return "\"ErrorType\": \""
- + ErrorType
- + "\","
- + "\"ErrorDetails\": [\""
- + String.join("\", \"", errorDetails)
- + "\"],"
- + "\"RetryArray\": [\""
- + String.join("\", \"", retryArray)
- + "\"],"
- + "\"UnthrottlingEvents\": [\""
- + String.join("\", \"", unthrottlingEvents)
- + "\"]";
- }
-
- private void sleep(long time) {
- try {
- Thread.sleep(time);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ private void advanceClockByTimeMs(long time) {
+ mMockedClockTime += time;
+ mTestLooper.dispatchAll();
}
private void setupMockForCarrierConfig(PersistableBundle bundle) {
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index a0414de..2f8f2c3 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -17,21 +17,46 @@
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.*;
+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;
import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.*;
+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;
+import static org.mockito.Mockito.verify;
+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;
import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
@@ -49,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;
@@ -65,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;
@@ -79,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";
@@ -96,26 +124,29 @@ public class IwlanDataServiceTest {
@Mock private EpdgTunnelManager mMockEpdgTunnelManager;
@Mock private IwlanDataServiceProvider mMockIwlanDataServiceProvider;
@Mock private Network mMockNetwork;
- @Mock private NetworkCapabilities mMockNetworkCapabilities;
@Mock private TunnelLinkProperties mMockTunnelLinkProperties;
@Mock private ErrorPolicyManager mMockErrorPolicyManager;
@Mock private ImsManager mMockImsManager;
@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;
+ private ArgumentCaptor<NetworkCallback> mNetworkCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
private final class IwlanDataServiceCallback extends IDataServiceCallback.Stub {
@@ -170,24 +201,32 @@ public class IwlanDataServiceTest {
mStaticMockSession =
mockitoSession()
.mockStatic(EpdgSelector.class)
+ .mockStatic(EpdgTunnelManager.class)
.mockStatic(ErrorPolicyManager.class)
.mockStatic(IwlanBroadcastReceiver.class)
- .mockStatic(IwlanHelper.class)
+ .mockStatic(SubscriptionManager.class)
.strictness(Strictness.LENIENT)
.startMocking();
when(mMockContext.getSystemService(eq(ConnectivityManager.class)))
.thenReturn(mMockConnectivityManager);
- when(mMockConnectivityManager.getNetworkCapabilities(eq(mMockNetwork)))
- .thenReturn(mMockNetworkCapabilities);
- when(mMockNetworkCapabilities.hasTransport(eq(TRANSPORT_CELLULAR))).thenReturn(false);
- when(mMockNetworkCapabilities.hasTransport(eq(TRANSPORT_WIFI))).thenReturn(true);
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);
+ when(mMockSubscriptionManager.getSlotIndex(DEFAULT_SUB_INDEX))
+ .thenReturn(DEFAULT_SLOT_INDEX);
+ when(mMockSubscriptionManager.getSlotIndex(DEFAULT_SUB_INDEX + 1))
+ .thenReturn(DEFAULT_SLOT_INDEX + 1);
when(mMockSubscriptionInfo.getSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX);
@@ -214,50 +253,335 @@ 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(mIwlanDataServiceProvider);
+ mSpyIwlanDataServiceProvider =
+ spy(
+ (IwlanDataServiceProvider)
+ mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX));
+ mTestLooper.dispatchAll();
+
+ 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();
}
}
- @Test
- public void testWifiOnAvailable() {
- IwlanNetworkMonitorCallback mNetworkMonitorCallback =
- mIwlanDataService.getNetworkMonitorCallback();
+ 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();
+ }
- mNetworkMonitorCallback.onAvailable(mMockNetwork);
- boolean ret = mIwlanDataService.isNetworkConnected(true, false);
+ 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();
+ }
+
+ private void onSystemDefaultNetworkConnected(int transportType) {
+ Network newNetwork = createMockNetwork(mLinkProperties);
+ onSystemDefaultNetworkConnected(
+ newNetwork, mLinkProperties, transportType, DEFAULT_SUB_INDEX);
+ }
- assertTrue(ret);
+ private void onSystemDefaultNetworkLost() {
+ NetworkCallback networkMonitorCallback = getNetworkMonitorCallback();
+ networkMonitorCallback.onLost(mMockNetwork);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testWifiOnConnected() {
+ onSystemDefaultNetworkConnected(TRANSPORT_WIFI);
+ assertTrue(
+ mIwlanDataService.isNetworkConnected(
+ false /* isActiveDataOnOtherSub */, false /* isCstEnabled */));
}
@Test
public void testWifiOnLost() {
+ when(mMockIwlanDataServiceProvider.getSlotIndex()).thenReturn(DEFAULT_SLOT_INDEX + 1);
mIwlanDataService.addIwlanDataServiceProvider(mMockIwlanDataServiceProvider);
- IwlanNetworkMonitorCallback mNetworkMonitorCallback =
- mIwlanDataService.getNetworkMonitorCallback();
- mNetworkMonitorCallback.onLost(mMockNetwork);
- boolean ret = mIwlanDataService.isNetworkConnected(true, false);
-
- assertFalse(ret);
+ onSystemDefaultNetworkLost();
+ assertFalse(
+ mIwlanDataService.isNetworkConnected(
+ false /* isActiveDataOnOtherSub */, false /* isCstEnabled */));
verify(mMockIwlanDataServiceProvider).forceCloseTunnelsInDeactivatingState();
mIwlanDataService.removeDataServiceProvider(mMockIwlanDataServiceProvider);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ 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 */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+ boolean isActiveDataOnOtherSub =
+ mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+ assertFalse(isActiveDataOnOtherSub);
+ assertFalse(
+ mIwlanDataService.isNetworkConnected(
+ isActiveDataOnOtherSub, true /* isCstEnabled */));
+ }
+
+ @Test
+ public void testCrossSimNetworkConnectedWithCellularOnDifferentSub()
+ throws InterruptedException {
+ NetworkCapabilities nc =
+ prepareNetworkCapabilitiesForTest(
+ TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+ boolean isActiveDataOnOtherSub =
+ mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+ assertTrue(isActiveDataOnOtherSub);
+ assertTrue(
+ mIwlanDataService.isNetworkConnected(
+ isActiveDataOnOtherSub, true /* isCstEnabled */));
+ }
+
+ @Test
+ public void testCrossSimNetworkConnectedWithVcnCellularOnDifferentSub()
+ throws InterruptedException {
+ NetworkCapabilities nc =
+ prepareNetworkCapabilitiesForTest(
+ TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, true /* isVcn */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+
+ boolean isActiveDataOnOtherSub =
+ mIwlanDataService.isActiveDataOnOtherSub(DEFAULT_SLOT_INDEX);
+
+ assertTrue(isActiveDataOnOtherSub);
+ assertTrue(
+ mIwlanDataService.isNetworkConnected(
+ isActiveDataOnOtherSub, true /* isCstEnabled */));
+ }
+
+ @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);
+ onSystemDefaultNetworkLost();
+
+ 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(
+ IllegalStateException.class,
+ () -> mIwlanDataService.addIwlanDataServiceProvider(mMockIwlanDataServiceProvider));
+ }
+
+ @Test
+ public void testRemoveDataServiceProvider() {
+ when(mMockIwlanDataServiceProvider.getSlotIndex()).thenReturn(DEFAULT_SLOT_INDEX);
+ mIwlanDataService.removeDataServiceProvider(mMockIwlanDataServiceProvider);
+ mTestLooper.dispatchAll();
+ verify(mIwlanDataService, times(1)).deinitNetworkCallback();
+ mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX);
+ mTestLooper.dispatchAll();
}
@Test
public void testRequestDataCallListPass() throws Exception {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
List<LinkAddress> mInternalAddressList;
List<InetAddress> mDNSAddressList;
List<InetAddress> mGatewayAddressList;
@@ -267,14 +591,16 @@ public class IwlanDataServiceTest {
IwlanDataServiceCallback callback = new IwlanDataServiceCallback("requestDataCallList");
TunnelLinkProperties mLinkProperties =
TunnelLinkPropertiesTest.createTestTunnelLinkProperties();
- mIwlanDataServiceProvider.setTunnelState(
+ mSpyIwlanDataServiceProvider.setTunnelState(
dp,
new DataServiceCallback(callback),
TunnelState.TUNNEL_UP,
mLinkProperties,
- false,
- 1);
- mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
+ mSpyIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
+ mTestLooper.dispatchAll();
latch.await(1, TimeUnit.SECONDS);
assertEquals(mResultCode, DataServiceCallback.RESULT_SUCCESS);
@@ -318,7 +644,8 @@ 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);
assertEquals(mResultCode, DataServiceCallback.RESULT_SUCCESS);
@@ -327,7 +654,7 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanSetupDataCallWithInvalidArg() {
- mIwlanDataServiceProvider.setupDataCall(
+ mSpyIwlanDataServiceProvider.setupDataCall(
AccessNetworkType.UNKNOWN, /* AccessNetworkType */
null, /* dataProfile */
false, /* isRoaming */
@@ -339,6 +666,7 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, timeout(1000).times(1))
.onSetupDataCallComplete(
@@ -347,13 +675,12 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanSetupDataCallWithIllegalState() {
- DataProfile dp = buildDataProfile();
+ 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 */
@@ -365,18 +692,21 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, timeout(1000).times(1))
.onSetupDataCallComplete(
- eq(5 /*DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */), isNull());
+ eq(5 /*DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */),
+ isNull());
}
@Test
public void testIwlanDeactivateDataCallWithInvalidArg() {
- mIwlanDataServiceProvider.deactivateDataCall(
+ mSpyIwlanDataServiceProvider.deactivateDataCall(
0, /* cid */
DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, timeout(1000).times(1))
.onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_ERROR_INVALID_ARG));
@@ -384,12 +714,11 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanSetupDataCallWithBringUpTunnel() {
- DataProfile dp = buildDataProfile();
+ 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 */
@@ -403,15 +732,20 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
- .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
/* Check callback result is RESULT_SUCCESS when onOpened() is called. */
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -419,12 +753,11 @@ public class IwlanDataServiceTest {
@Test
public void testSliceInfoInclusionInDataCallResponse() throws Exception {
- DataProfile dp = buildDataProfile();
+ 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 */
@@ -438,10 +771,14 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
- .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
/* Check callback result is RESULT_SUCCESS when onOpened() is called. */
TunnelLinkProperties tp = TunnelLinkPropertiesTest.createTestTunnelLinkProperties();
@@ -450,6 +787,7 @@ public class IwlanDataServiceTest {
ArgumentCaptor.forClass(DataCallResponse.class);
mSpyIwlanDataServiceProvider.getIwlanTunnelCallback().onOpened(TEST_APN_NAME, tp);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
@@ -462,32 +800,81 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanDeactivateDataCallWithCloseTunnel() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
- doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
+ onSystemDefaultNetworkConnected(TRANSPORT_WIFI);
mSpyIwlanDataServiceProvider.setTunnelState(
- dp, mMockDataServiceCallback, TunnelState.TUNNEL_IN_BRINGUP, null, false, 1);
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
- DataService.REQUEST_REASON_NORMAL /* DataService.REQUEST_REASON_NORMAL */,
+ DataService.REQUEST_REASON_NORMAL,
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
+ /* Check closeTunnel() is called. */
+ verify(mMockEpdgTunnelManager, times(1))
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(false),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
+ /* Check callback result is RESULT_SUCCESS when onClosed() is called. */
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+ verify(mMockDataServiceCallback, times(1))
+ .onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS));
+ }
+
+ @Test
+ public void testIwlanDeactivateDataCallAfterSuccessHandover() {
+ DataProfile dp = buildImsDataProfile();
+
+ onSystemDefaultNetworkConnected(TRANSPORT_WIFI);
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ false, /* isHandover */
+ 1, /* pduSessionId */
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.deactivateDataCall(
+ TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
+ DataService.REQUEST_REASON_HANDOVER,
+ mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check closeTunnel() is called. */
- verify(mMockEpdgTunnelManager, times(1)).closeTunnel(eq(TEST_APN_NAME), anyBoolean());
+ verify(mMockEpdgTunnelManager, times(1))
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(true),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
/* Check callback result is RESULT_SUCCESS when onClosed() is called. */
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS));
}
@Test
- public void testHandoverFailureModeNormal() {
- DataProfile dp = buildDataProfile();
+ public void testHandoverFailureModeDefault() {
+ DataProfile dp = buildImsDataProfile();
int setupDataReason = DataService.REQUEST_REASON_NORMAL;
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
@@ -500,13 +887,25 @@ public class IwlanDataServiceTest {
dp,
mMockDataServiceCallback,
TunnelState.TUNNEL_IN_BRINGUP,
- null,
+ null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
- 1);
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
ArgumentCaptor.forClass(DataCallResponse.class);
@@ -518,14 +917,14 @@ public class IwlanDataServiceTest {
DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
assertEquals(
dataCallResponse.getHandoverFailureMode(),
- DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY);
assertEquals(dataCallResponse.getCause(), DataFailCause.USER_AUTHENTICATION);
assertEquals(dataCallResponse.getRetryDurationMillis(), 5L);
}
@Test
public void testHandoverFailureModeHandover() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
@@ -533,18 +932,32 @@ public class IwlanDataServiceTest {
when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
.thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(false);
mSpyIwlanDataServiceProvider.setTunnelState(
dp,
mMockDataServiceCallback,
TunnelState.TUNNEL_IN_BRINGUP,
- null,
+ null, /* linkProperties */
(setupDataReason == DataService.REQUEST_REASON_HANDOVER),
- 1);
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
ArgumentCaptor.forClass(DataCallResponse.class);
@@ -562,34 +975,268 @@ public class IwlanDataServiceTest {
}
@Test
- public void testDnsPrefetching() throws Exception {
- IwlanNetworkMonitorCallback mNetworkMonitorCallback =
- mIwlanDataService.getNetworkMonitorCallback();
- /* Wifi is connected */
- mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
+ public void testSupportInitialAttachSuccessOnIms() {
+ DataProfile dp = buildImsDataProfile();
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = IMS, in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_IDLE)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
- List<LinkAddress> linkAddresses = new ArrayList<>();
- linkAddresses.add(mMockIPv4LinkAddress);
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
- when(mMockLinkProperties.getLinkAddresses()).thenReturn(linkAddresses);
- mNetworkMonitorCallback.onLinkPropertiesChanged(mMockNetwork, mMockLinkProperties);
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // Not on video or voice call
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL,
+ dataCallResponse.getHandoverFailureMode());
+ }
- mIwlanDataServiceProvider
- .mHandler
- .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ @Test
+ public void testSupportInitialAttachSuccessOnEmergency() {
+ DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = Emergency, in idle call state
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_IDLE)
.sendToTarget();
- sleep(1000);
- mIwlanDataServiceProvider
- .mHandler
- .obtainMessage(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT)
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null, /* linkProperties */
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 512, // type Emergency
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // Not on video or voice call
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testSupportInitialAttachOnImsCall() {
+ DataProfile dp = buildImsDataProfile();
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = IMS, in call
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_OFFHOOK)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null /* linkProperties */,
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 64, // type IMS
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // In call state
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testSupportInitialAttachOnEmergencyCall() {
+ DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
+ int setupDataReason = DataService.REQUEST_REASON_HANDOVER;
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getCurrentRetryTimeMs(eq(TEST_APN_NAME))).thenReturn(-1L);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+ when(mMockErrorPolicyManager.shouldRetryWithInitialAttach(eq(TEST_APN_NAME)))
+ .thenReturn(true);
+
+ // APN = Emergency, in call
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CALL_STATE_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ TelephonyManager.CALL_STATE_OFFHOOK)
+ .sendToTarget();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_IN_BRINGUP,
+ null /* linkProperties */,
+ (setupDataReason == DataService.REQUEST_REASON_HANDOVER),
+ 1 /* pduSessionId */,
+ true /* isImsOrEmergency */);
+
+ mSpyIwlanDataServiceProvider.setMetricsAtom(
+ TEST_APN_NAME,
+ 512, // type Emergency
+ true,
+ 13, // LTE
+ false,
+ true,
+ 1 // Transport Wi-Fi
+ );
+
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+ ArgumentCaptor.forClass(DataCallResponse.class);
+ verify(mMockDataServiceCallback, times(1))
+ .onSetupDataCallComplete(
+ eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+ DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+ // In call state
+ assertEquals(
+ DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+ dataCallResponse.getHandoverFailureMode());
+ }
+
+ @Test
+ public void testDnsPrefetching() throws Exception {
+ NetworkCallback networkCallback = getNetworkMonitorCallback();
+ /* Wifi is connected */
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
+ networkCallback.onLinkPropertiesChanged(mMockNetwork, mLinkProperties);
+
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ 0 /* unused */)
+ .sendToTarget();
+
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.WIFI_CALLING_ENABLE_EVENT,
+ DEFAULT_SLOT_INDEX,
+ 0 /* unused */)
.sendToTarget();
- sleep(1000);
+ 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
@@ -599,6 +1246,7 @@ public class IwlanDataServiceTest {
.getValidatedServerList(
eq(0),
eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ eq(EpdgSelector.SYSTEM_PREFERRED),
eq(false),
eq(false),
eq(mMockNetwork),
@@ -607,21 +1255,26 @@ public class IwlanDataServiceTest {
.getValidatedServerList(
eq(0),
eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ eq(EpdgSelector.SYSTEM_PREFERRED),
eq(false),
eq(true),
eq(mMockNetwork),
isNull());
}
- private void sleep(long time) {
- try {
- Thread.sleep(time);
- } catch (Exception e) {
- e.printStackTrace();
+ private void advanceCalendarByTimeMs(long time, Calendar calendar) {
+ mMockedCalendarTime += time;
+ if (calendar != null) {
+ calendar.setTimeInMillis(mMockedCalendarTime);
}
+ mTestLooper.dispatchAll();
}
- private DataProfile buildDataProfile() {
+ private DataProfile buildImsDataProfile() {
+ return buildDataProfile(ApnSetting.TYPE_IMS);
+ }
+
+ private DataProfile buildDataProfile(int supportedApnTypesBitmask) {
DataProfile dp =
new DataProfile.Builder()
.setProfileId(1)
@@ -635,7 +1288,7 @@ public class IwlanDataServiceTest {
// .setMaxConnections(3)
// .setWaitTime(10)
.enable(true)
- .setSupportedApnTypesBitmask(ApnSetting.TYPE_IMS)
+ .setSupportedApnTypesBitmask(supportedApnTypesBitmask)
.setRoamingProtocolType(ApnSetting.PROTOCOL_IPV4V6) // IPv4v6
.setBearerBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
.setPersistent(true)
@@ -644,24 +1297,30 @@ public class IwlanDataServiceTest {
return dp;
}
- @Test
- public void testIwlanSetupDataCallWithCellularAndCstDisabled() {
- DataProfile dp = buildDataProfile();
+ private NetworkCapabilities prepareNetworkCapabilitiesForTest(
+ int transportType, int subId, boolean isVcn) {
+ NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder().addTransportType(transportType);
+ if (isVcn) {
+ builder.setTransportInfo(new VcnTransportInfo(subId));
+ } else {
+ builder.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+ }
+ return builder.build();
+ }
- /* Mobile is connected */
- mIwlanDataService.setNetworkConnected(
- true, mMockNetwork, IwlanDataService.Transport.MOBILE);
+ @Test
+ public void testIwlanSetupDataCallFailsWithCellularAndCstDisabled() throws Exception {
+ DataProfile dp = buildImsDataProfile();
+ /* CST is disabled, and data is on the same sub as the data service provider */
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(false);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
+ NetworkCapabilities nc =
+ prepareNetworkCapabilitiesForTest(
+ TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
- mIwlanDataServiceProvider.setupDataCall(
+ mSpyIwlanDataServiceProvider.setupDataCall(
AccessNetworkType.IWLAN, /* AccessNetworkType */
dp, /* dataProfile */
false, /* isRoaming */
@@ -673,34 +1332,58 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, timeout(1000).times(1))
.onSetupDataCallComplete(
- eq(5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */), isNull());
+ eq(5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */),
+ isNull());
}
@Test
- public void testIwlanSetupDataCallWithCellularAndCstEnabled() {
- DataProfile dp = buildDataProfile();
+ public void testIwlanSetupDataCallFailsWithCellularOnSameSubAndCstEnabled() throws Exception {
+ DataProfile dp = buildImsDataProfile();
- /* Clear state */
- mIwlanDataService.setNetworkConnected(
- false, mMockNetwork, IwlanDataService.Transport.UNSPECIFIED_NETWORK);
+ /* CST is enabled, but data is on the same sub as the DataServiceProvider */
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
- /* Mobile is connected */
- mIwlanDataService.setNetworkConnected(
- true, mMockNetwork, IwlanDataService.Transport.MOBILE);
+ NetworkCapabilities nc =
+ prepareNetworkCapabilitiesForTest(
+ TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX, false /* isVcn */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
- doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
+ mSpyIwlanDataServiceProvider.setupDataCall(
+ AccessNetworkType.IWLAN, /* AccessNetworkType */
+ dp, /* dataProfile */
+ false, /* isRoaming */
+ true, /* allowRoaming */
+ DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */
+ null, /* LinkProperties */
+ 1, /* pduSessionId */
+ null, /* sliceInfo */
+ null, /* trafficDescriptor */
+ true, /* matchAllRuleAllowed */
+ mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(false);
+ verify(mMockDataServiceCallback, timeout(1000).times(1))
+ .onSetupDataCallComplete(
+ eq(5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */),
+ isNull());
+ }
+
+ @Test
+ public void testIwlanSetupDataCallSucceedsWithCellularOnDifferentSubAndCstEnabled()
+ throws Exception {
+ DataProfile dp = buildImsDataProfile();
+
+ /* CST is enabled, but data is on the same sub as the DataServiceProvider */
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
+
+ NetworkCapabilities nc =
+ prepareNetworkCapabilitiesForTest(
+ TRANSPORT_CELLULAR, DEFAULT_SUB_INDEX + 1, false /* isVcn */);
+ getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
mSpyIwlanDataServiceProvider.setupDataCall(
AccessNetworkType.IWLAN, /* AccessNetworkType */
@@ -714,15 +1397,20 @@ public class IwlanDataServiceTest {
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
- .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
/* Check callback result is RESULT_SUCCESS when onOpened() is called. */
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -730,17 +1418,18 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanTunnelStatsFailureCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
+
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
- mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
- doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
.thenReturn(mMockErrorPolicyManager);
long count = 3L;
for (int i = 0; i < count; i++) {
mockTunnelSetupFail(dp);
- sleep(1000);
+ mTestLooper.dispatchAll();
}
IwlanDataServiceProvider.IwlanDataTunnelStats stats =
@@ -751,14 +1440,19 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanTunnelStatsUnsolDownCounts() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
- mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
- doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
long count = 3L;
for (int i = 0; i < count; i++) {
- mockTunnelSetupSuccess(dp, 0);
+ mockTunnelSetupSuccess(dp, 0, null);
mockUnsolTunnelDown();
}
@@ -770,35 +1464,38 @@ public class IwlanDataServiceTest {
@Test
public void testIwlanTunnelStats() {
- DataProfile dp = buildDataProfile();
+ DataProfile dp = buildImsDataProfile();
+ Calendar calendar = mock(Calendar.class);
+ when(calendar.getTime()).thenAnswer(i -> new Date(mMockedCalendarTime));
- mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
- doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
+ mSpyIwlanDataServiceProvider.setCalendar(calendar);
+ onSystemDefaultNetworkConnected(
+ mMockNetwork, mLinkProperties, TRANSPORT_WIFI, INVALID_SUB_INDEX);
LongSummaryStatistics tunnelSetupSuccessStats = new LongSummaryStatistics();
LongSummaryStatistics tunnelUpStats = new LongSummaryStatistics();
- Date beforeSetup = Calendar.getInstance().getTime();
- mockTunnelSetupSuccess(dp, 0);
- Date tunnelUp = Calendar.getInstance().getTime();
- mockDeactivateTunnelDown(0);
- Date tunnelDown = Calendar.getInstance().getTime();
+ Date beforeSetup = calendar.getTime();
+ mockTunnelSetupSuccess(dp, 0, calendar);
+ Date tunnelUp = calendar.getTime();
+ mockDeactivateTunnel(0, calendar);
+ Date tunnelDown = calendar.getTime();
tunnelSetupSuccessStats.accept(tunnelUp.getTime() - beforeSetup.getTime());
tunnelUpStats.accept(tunnelDown.getTime() - tunnelUp.getTime());
- beforeSetup = Calendar.getInstance().getTime();
- mockTunnelSetupSuccess(dp, 1000);
- tunnelUp = Calendar.getInstance().getTime();
- mockDeactivateTunnelDown(3000);
- tunnelDown = Calendar.getInstance().getTime();
+ beforeSetup = calendar.getTime();
+ mockTunnelSetupSuccess(dp, 1000, calendar);
+ tunnelUp = calendar.getTime();
+ mockDeactivateTunnel(3000, calendar);
+ tunnelDown = calendar.getTime();
tunnelSetupSuccessStats.accept(tunnelUp.getTime() - beforeSetup.getTime());
tunnelUpStats.accept(tunnelDown.getTime() - tunnelUp.getTime());
- beforeSetup = Calendar.getInstance().getTime();
- mockTunnelSetupSuccess(dp, 600);
- tunnelUp = Calendar.getInstance().getTime();
- mockDeactivateTunnelDown(500);
- tunnelDown = Calendar.getInstance().getTime();
+ beforeSetup = calendar.getTime();
+ mockTunnelSetupSuccess(dp, 600, calendar);
+ tunnelUp = calendar.getTime();
+ mockDeactivateTunnel(500, calendar);
+ tunnelDown = calendar.getTime();
tunnelSetupSuccessStats.accept(tunnelUp.getTime() - beforeSetup.getTime());
tunnelUpStats.accept(tunnelDown.getTime() - tunnelUp.getTime());
@@ -807,13 +1504,182 @@ public class IwlanDataServiceTest {
LongSummaryStatistics finalSetupStats = stats.mTunnelSetupSuccessStats.get(TEST_APN_NAME);
LongSummaryStatistics finalUpStats = stats.mTunnelUpStats.get(TEST_APN_NAME);
- assertEquals(finalSetupStats.getAverage(), tunnelSetupSuccessStats.getAverage(), 100);
- assertEquals(finalSetupStats.getCount(), tunnelSetupSuccessStats.getCount());
- assertEquals(finalSetupStats.getMax(), tunnelSetupSuccessStats.getMax(), 100);
+ assertEquals(tunnelSetupSuccessStats.getAverage(), finalSetupStats.getAverage(), 0);
+ assertEquals(tunnelSetupSuccessStats.getCount(), finalSetupStats.getCount());
+ assertEquals(tunnelSetupSuccessStats.getMax(), finalSetupStats.getMax(), 0);
+
+ assertEquals(tunnelUpStats.getAverage(), finalUpStats.getAverage(), 0);
+ assertEquals(tunnelUpStats.getCount(), finalUpStats.getCount());
+ assertEquals(tunnelUpStats.getMax(), finalUpStats.getMax(), 0);
+ }
- assertEquals(finalUpStats.getAverage(), tunnelUpStats.getAverage(), 100);
- assertEquals(finalUpStats.getCount(), tunnelUpStats.getCount());
- assertEquals(finalUpStats.getMax(), tunnelUpStats.getMax(), 100);
+ @Test
+ public void testUnexpectedTunnelClosedIsSuppressed() {
+ mockUnsolTunnelDown();
+ }
+
+ @Test
+ public void testIwlanDataServiceHandlerOnUnbind() {
+ DataProfile dp = buildImsDataProfile();
+
+ mSpyIwlanDataServiceProvider.setTunnelState(
+ dp,
+ mMockDataServiceCallback,
+ TunnelState.TUNNEL_UP,
+ 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
+ );
+
+ when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
+ .thenReturn(mMockErrorPolicyManager);
+ when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
+ .thenReturn(DataFailCause.ERROR_UNSPECIFIED);
+
+ // Simulate IwlanDataService.onUnbind() which force close all tunnels
+ mSpyIwlanDataServiceProvider.forceCloseTunnels();
+ // Simulate DataService.onUnbind() which remove all IwlanDataServiceProviders
+ mSpyIwlanDataServiceProvider.close();
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgTunnelManager, atLeastOnce())
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ eq(true),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
+ assertNotNull(mIwlanDataService.mIwlanDataServiceHandler);
+ // Should not raise NullPointerException
+ mSpyIwlanDataServiceProvider
+ .getIwlanTunnelCallback()
+ .onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ 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) {
@@ -831,17 +1697,21 @@ public class IwlanDataServiceTest {
mMockDataServiceCallback);
doReturn(true)
.when(mMockEpdgTunnelManager)
- .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.IKE_INTERNAL_IO_EXCEPTION));
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, atLeastOnce())
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
}
- private void mockTunnelSetupSuccess(DataProfile dp, long sleepTime) {
+ private void mockTunnelSetupSuccess(DataProfile dp, long setupTime, Calendar calendar) {
mSpyIwlanDataServiceProvider.setupDataCall(
AccessNetworkType.IWLAN, /* AccessNetworkType */
dp, /* dataProfile */
@@ -856,13 +1726,18 @@ public class IwlanDataServiceTest {
mMockDataServiceCallback);
doReturn(true)
.when(mMockEpdgTunnelManager)
- .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ .bringUpTunnel(
+ any(TunnelSetupRequest.class),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
+ mTestLooper.dispatchAll();
- sleep(sleepTime);
+ advanceCalendarByTimeMs(setupTime, calendar);
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, atLeastOnce())
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -872,21 +1747,37 @@ public class IwlanDataServiceTest {
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.IKE_INTERNAL_IO_EXCEPTION));
+ mTestLooper.dispatchAll();
}
- private void mockDeactivateTunnelDown(long sleepTime) {
+ private void mockDeactivateTunnel(long deactivationTime, Calendar calendar) {
mSpyIwlanDataServiceProvider.deactivateDataCall(
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
DataService.REQUEST_REASON_NORMAL /* DataService.REQUEST_REASON_NORMAL */,
mMockDataServiceCallback);
- verify(mMockEpdgTunnelManager, atLeastOnce()).closeTunnel(eq(TEST_APN_NAME), anyBoolean());
+ mTestLooper.dispatchAll();
+ verify(mMockEpdgTunnelManager, atLeastOnce())
+ .closeTunnel(
+ eq(TEST_APN_NAME),
+ anyBoolean(),
+ any(IwlanTunnelCallback.class),
+ any(IwlanTunnelMetricsImpl.class));
- sleep(sleepTime);
+ advanceCalendarByTimeMs(deactivationTime, calendar);
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
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/IwlanEventListenerTest.java b/test/com/google/android/iwlan/IwlanEventListenerTest.java
index 789ab25..79d53c0 100644
--- a/test/com/google/android/iwlan/IwlanEventListenerTest.java
+++ b/test/com/google/android/iwlan/IwlanEventListenerTest.java
@@ -18,7 +18,6 @@ package com.google.android.iwlan;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import android.content.ContentResolver;
@@ -93,15 +92,14 @@ public class IwlanEventListenerTest {
when(mMockContext.getSystemService(eq(SubscriptionManager.class)))
.thenReturn(mMockSubscriptionManager);
- when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
- eq(DEFAULT_SLOT_INDEX)))
+ when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt()))
.thenReturn(mMockSubscriptionInfo);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockImsMmTelManager.isVoWiFiSettingEnabled()).thenReturn(true).thenReturn(false);
- when(mMockImsManager.getImsMmTelManager(eq(2))).thenReturn(mMockImsMmTelManager);
+ when(mMockImsManager.getImsMmTelManager(anyInt())).thenReturn(mMockImsMmTelManager);
when(mMockContext.getSystemService(eq(ImsManager.class))).thenReturn(mMockImsManager);
@@ -111,6 +109,7 @@ public class IwlanEventListenerTest {
when(mMockTelephonyManager.createForSubscriptionId(eq(0)))
.thenReturn(mMockTelephonyManager);
+ IwlanEventListener.resetAllInstances();
mIwlanEventListener = IwlanEventListener.getInstance(mMockContext, DEFAULT_SLOT_INDEX);
}
@@ -121,7 +120,10 @@ public class IwlanEventListenerTest {
@Test
public void testWifiApChanged() throws Exception {
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.WIFI_AP_CHANGED_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.WIFI_AP_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage);
events = new ArrayList<Integer>();
@@ -140,7 +142,10 @@ public class IwlanEventListenerTest {
@Test
public void testCrossSimCallingSettingEnableChanged() throws Exception {
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage);
events = new ArrayList<Integer>();
@@ -161,7 +166,10 @@ public class IwlanEventListenerTest {
@Test
public void testCrossSimCallingSettingDisableChanged() throws Exception {
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage);
events = new ArrayList<Integer>();
@@ -182,10 +190,15 @@ public class IwlanEventListenerTest {
@Test
public void testOnReceivedCarrierConfigChangedIntent() throws Exception {
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage);
when(mMockHandler.obtainMessage(
- eq(IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT)))
+ eq(IwlanEventListener.CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage_2);
events = new ArrayList<Integer>();
@@ -217,9 +230,15 @@ public class IwlanEventListenerTest {
@Test
public void testWfcSettingChanged() throws Exception {
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage);
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
.thenReturn(mMockMessage_2);
events = new ArrayList<Integer>();
@@ -242,7 +261,11 @@ public class IwlanEventListenerTest {
.when(IwlanHelper.getSubId(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
.thenReturn(0);
- when(mMockHandler.obtainMessage(eq(IwlanEventListener.CELLINFO_CHANGED_EVENT), eq(arrayCi)))
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CELLINFO_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt(),
+ eq(arrayCi)))
.thenReturn(mMockMessage);
events = new ArrayList<Integer>();
@@ -257,4 +280,46 @@ public class IwlanEventListenerTest {
verify(mMockMessage, times(1)).sendToTarget();
}
+
+ @Test
+ public void testCallStateChanged() throws Exception {
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.CALL_STATE_CHANGED_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ eq(TelephonyManager.CALL_STATE_OFFHOOK)))
+ .thenReturn(mMockMessage);
+
+ events = new ArrayList<Integer>();
+ events.add(IwlanEventListener.CALL_STATE_CHANGED_EVENT);
+ mIwlanEventListener.addEventListener(events, mMockHandler);
+
+ mIwlanEventListener.registerTelephonyCallback();
+
+ TelephonyCallback.CallStateListener mTelephonyCallback =
+ mIwlanEventListener.getTelephonyCallback();
+ mTelephonyCallback.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ verify(mMockMessage, times(1)).sendToTarget();
+ }
+
+ @Test
+ public void testWfcChangeThrowIAE() throws Exception {
+ when(mMockHandler.obtainMessage(
+ eq(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT),
+ eq(DEFAULT_SLOT_INDEX),
+ anyInt()))
+ .thenReturn(mMockMessage);
+
+ events = new ArrayList<Integer>();
+ events.add(IwlanEventListener.WIFI_CALLING_DISABLE_EVENT);
+ mIwlanEventListener.addEventListener(events, mMockHandler);
+ mIwlanEventListener.setWfcEnabledUri(WFC_ENABLED_URI);
+
+ doThrow(new IllegalArgumentException("IllegalArgumentException at isVoWiFiSettingEnabled"))
+ .when(mMockImsMmTelManager)
+ .isVoWiFiSettingEnabled();
+
+ mIwlanEventListener.notifyCurrentSetting(WFC_ENABLED_URI);
+ verify(mMockMessage, times(1)).sendToTarget();
+ }
}
diff --git a/test/com/google/android/iwlan/IwlanNetworkServiceTest.java b/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
index 000190a..0633900 100644
--- a/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanNetworkServiceTest.java
@@ -21,8 +21,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnTransportInfo;
import android.telephony.AccessNetworkConstants;
import android.telephony.INetworkService;
import android.telephony.INetworkServiceCallback;
@@ -31,6 +36,8 @@ import android.telephony.NetworkServiceCallback;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
import com.google.android.iwlan.IwlanNetworkService.IwlanNetworkServiceProvider;
@@ -40,18 +47,23 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.Arrays;
public class IwlanNetworkServiceTest {
private static final String TAG = IwlanNetworkServiceTest.class.getSimpleName();
private static final int DEFAULT_SLOT_INDEX = 0;
+ private static final int DEFAULT_SUB_INDEX = 0;
@Mock private Context mMockContext;
@Mock private ConnectivityManager mMockConnectivityManager;
@Mock private SubscriptionManager mMockSubscriptionManager;
@Mock private SubscriptionInfo mMockSubscriptionInfo;
+ @Mock private ImsManager mMockImsManager;
+ @Mock private ImsMmTelManager mMockImsMmTelManager;
@Mock private INetworkServiceCallback mCallback;
+ @Mock private Network mMockNetwork;
MockitoSession mStaticMockSession;
IwlanNetworkService mIwlanNetworkService;
@@ -64,8 +76,8 @@ public class IwlanNetworkServiceTest {
mStaticMockSession =
mockitoSession()
- .mockStatic(IwlanHelper.class)
.mockStatic(SubscriptionManager.class)
+ .strictness(Strictness.LENIENT)
.startMocking();
when(mMockContext.getSystemService(eq(ConnectivityManager.class)))
@@ -77,10 +89,17 @@ public class IwlanNetworkServiceTest {
when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
eq(DEFAULT_SLOT_INDEX)))
.thenReturn(mMockSubscriptionInfo);
+ when(mMockSubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX);
+ when(mMockSubscriptionManager.getSlotIndex(DEFAULT_SUB_INDEX))
+ .thenReturn(DEFAULT_SLOT_INDEX);
+ when(mMockSubscriptionManager.getSlotIndex(DEFAULT_SUB_INDEX + 1))
+ .thenReturn(DEFAULT_SLOT_INDEX + 1);
- lenient()
- .when(SubscriptionManager.from(eq(mMockContext)))
- .thenReturn(mMockSubscriptionManager);
+ when(mMockSubscriptionInfo.getSubscriptionId()).thenReturn(DEFAULT_SUB_INDEX);
+
+ when(mMockContext.getSystemService(eq(ImsManager.class))).thenReturn(mMockImsManager);
+
+ when(mMockImsManager.getImsMmTelManager(anyInt())).thenReturn(mMockImsMmTelManager);
mIwlanNetworkService = new IwlanNetworkService();
mIwlanNetworkService.setAppContext(mMockContext);
@@ -97,22 +116,23 @@ public class IwlanNetworkServiceTest {
mStaticMockSession.finishMocking();
}
- @Test
- public void testRequestNetworkRegistrationInfo() throws Exception {
- int domain = NetworkRegistrationInfo.DOMAIN_PS;
- boolean mIsSubActive = true;
- long startTime;
-
+ @Nullable
+ IwlanNetworkServiceProvider initNSP() {
// Wait for IwlanNetworkServiceProvider created and timeout is 1 second.
- startTime = System.currentTimeMillis();
+ long startTime = System.currentTimeMillis();
+ IwlanNetworkServiceProvider nsp = null;
while (System.currentTimeMillis() - startTime < 1000) {
- mIwlanNetworkServiceProvider =
- mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
- if (mIwlanNetworkServiceProvider != null) {
+ nsp = mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
+ if (nsp != null) {
break;
}
}
+ return nsp;
+ }
+ @Test
+ public void testRequestNetworkRegistrationInfo() throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
assertTrue(mIwlanNetworkServiceProvider != null);
// Set Wifi on and verify mCallback should receive onNetworkStateChanged.
@@ -126,223 +146,202 @@ public class IwlanNetworkServiceTest {
// Create expected NetworkRegistrationInfo
NetworkRegistrationInfo.Builder expectedStateBuilder =
generateStateBuilder(
- domain, mIsSubActive, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
verify(mCallback, timeout(1000).times(1))
.onRequestNetworkRegistrationInfoComplete(
eq(NetworkServiceCallback.RESULT_SUCCESS),
eq(expectedStateBuilder.build()));
-
- IwlanNetworkService.setNetworkConnected(
- false, IwlanNetworkService.Transport.UNSPECIFIED_NETWORK);
}
- @Test
- public void testNetworkRegistrationInfoForCellularAndCstDisabled() throws Exception {
- int domain = NetworkRegistrationInfo.DOMAIN_PS;
- boolean mIsSubActive = true;
- long startTime;
-
- // Wait for IwlanNetworkServiceProvider created and timeout is 1 second.
- startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < 1000) {
- mIwlanNetworkServiceProvider =
- mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
- if (mIwlanNetworkServiceProvider != null) {
- break;
- }
+ private NetworkCapabilities prepareCellularNetworkCapabilitiesForTest(
+ int subId, boolean isVcn) {
+ NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ if (isVcn) {
+ builder.setTransportInfo(new VcnTransportInfo(subId));
+ } else {
+ builder.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
}
+ return builder.build();
+ }
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(false);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
+ private NetworkCapabilities prepareWifiNetworkCapabilitiesForTest() {
+ return new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+ @Test
+ public void testNetworkRegistrationInfoSearchingForCellularAndCstDisabled() throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
assertTrue(mIwlanNetworkServiceProvider != null);
- // Set Network on and verify mCallback should receive onNetworkStateChanged.
- mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.MOBILE);
- verify(mCallback, timeout(1000).times(1)).onNetworkStateChanged();
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
- // Set Sub active and verify mCallback should receive onNetworkStateChanged.
+ NetworkCapabilities nc =
+ prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX, false /* is Vcn */);
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
mIwlanNetworkServiceProvider.subscriptionChanged();
- verify(mCallback, timeout(1000).times(2)).onNetworkStateChanged();
// Create expected NetworkRegistrationInfo
NetworkRegistrationInfo.Builder expectedStateBuilder =
generateStateBuilder(
- domain,
- mIsSubActive,
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
- mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
verify(mCallback, timeout(1000).times(1))
.onRequestNetworkRegistrationInfoComplete(
eq(NetworkServiceCallback.RESULT_SUCCESS),
eq(expectedStateBuilder.build()));
-
- IwlanNetworkService.setNetworkConnected(
- false, IwlanNetworkService.Transport.UNSPECIFIED_NETWORK);
}
@Test
- public void testNetworkRegistrationInfoForCellularAndCstEnabled() throws Exception {
- int domain = NetworkRegistrationInfo.DOMAIN_PS;
- boolean mIsSubActive = true;
- long startTime;
-
- // Wait for IwlanNetworkServiceProvider created and timeout is 1 second.
- startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < 1000) {
- mIwlanNetworkServiceProvider =
- mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
- if (mIwlanNetworkServiceProvider != null) {
- break;
- }
- }
-
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(false);
-
+ public void testNetworkRegistrationInfoSearchingForCellularOnSameSubAndCstEnabled()
+ throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
assertTrue(mIwlanNetworkServiceProvider != null);
- // Set Network on and verify mCallback should receive onNetworkStateChanged.
- mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.MOBILE);
- verify(mCallback, timeout(1000).times(1)).onNetworkStateChanged();
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
- // Set Sub active and verify mCallback should receive onNetworkStateChanged.
+ NetworkCapabilities nc =
+ prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX, false /* is Vcn */);
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
mIwlanNetworkServiceProvider.subscriptionChanged();
- verify(mCallback, timeout(1000).times(2)).onNetworkStateChanged();
// Create expected NetworkRegistrationInfo
NetworkRegistrationInfo.Builder expectedStateBuilder =
generateStateBuilder(
- domain, mIsSubActive, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* mIsSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
- mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
verify(mCallback, timeout(1000).times(1))
.onRequestNetworkRegistrationInfoComplete(
eq(NetworkServiceCallback.RESULT_SUCCESS),
eq(expectedStateBuilder.build()));
-
- IwlanNetworkService.setNetworkConnected(
- false, IwlanNetworkService.Transport.UNSPECIFIED_NETWORK);
}
@Test
- public void testNetworkRegistrationInfoForWiFiAndCstEnabled() throws Exception {
- int domain = NetworkRegistrationInfo.DOMAIN_PS;
- boolean mIsSubActive = true;
- long startTime;
+ public void testNetworkRegistrationInfoHomeForCellularOnDifferentSubAndCstEnabled()
+ throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
+ assertTrue(mIwlanNetworkServiceProvider != null);
- // Wait for IwlanNetworkServiceProvider created and timeout is 1 second.
- startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < 1000) {
- mIwlanNetworkServiceProvider =
- mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
- if (mIwlanNetworkServiceProvider != null) {
- break;
- }
- }
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
+ // Cellular data is on the other sub
+ NetworkCapabilities nc =
+ prepareCellularNetworkCapabilitiesForTest(
+ DEFAULT_SUB_INDEX + 1, false /* is Vcn */);
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+ mIwlanNetworkServiceProvider.subscriptionChanged();
+ // Create expected NetworkRegistrationInfo
+ NetworkRegistrationInfo.Builder expectedStateBuilder =
+ generateStateBuilder(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+
+ verify(mCallback, timeout(1000).times(1))
+ .onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS),
+ eq(expectedStateBuilder.build()));
+ }
+
+ @Test
+ public void testNetworkRegistrationInfoHomeForCellularVcnOnDifferentSubAndCstEnabled()
+ throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
assertTrue(mIwlanNetworkServiceProvider != null);
- // Set Network on and verify mCallback should receive onNetworkStateChanged.
- mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.WIFI);
- verify(mCallback, timeout(1000).times(1)).onNetworkStateChanged();
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
- // Set Sub active and verify mCallback should receive onNetworkStateChanged.
+ // Cellular data as a VCN network is on the other sub
+ NetworkCapabilities nc =
+ prepareCellularNetworkCapabilitiesForTest(DEFAULT_SUB_INDEX + 1, true /* is Vcn */);
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
mIwlanNetworkServiceProvider.subscriptionChanged();
- verify(mCallback, timeout(1000).times(2)).onNetworkStateChanged();
// Create expected NetworkRegistrationInfo
NetworkRegistrationInfo.Builder expectedStateBuilder =
generateStateBuilder(
- domain, mIsSubActive, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
verify(mCallback, timeout(1000).times(1))
.onRequestNetworkRegistrationInfoComplete(
eq(NetworkServiceCallback.RESULT_SUCCESS),
eq(expectedStateBuilder.build()));
-
- IwlanNetworkService.setNetworkConnected(
- false, IwlanNetworkService.Transport.UNSPECIFIED_NETWORK);
}
@Test
- public void testNetworkRegistrationInfoForWiFiAndCstDisabled() throws Exception {
- int domain = NetworkRegistrationInfo.DOMAIN_PS;
- boolean mIsSubActive = true;
- long startTime;
+ public void testNetworkRegistrationInfoHomeForWiFiAndCstEnabled() throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
+ assertTrue(mIwlanNetworkServiceProvider != null);
- // Wait for IwlanNetworkServiceProvider created and timeout is 1 second.
- startTime = System.currentTimeMillis();
- while (System.currentTimeMillis() - startTime < 1000) {
- mIwlanNetworkServiceProvider =
- mIwlanNetworkService.getNetworkServiceProvider(DEFAULT_SLOT_INDEX);
- if (mIwlanNetworkServiceProvider != null) {
- break;
- }
- }
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(true);
- lenient()
- .when(
- IwlanHelper.isCrossSimCallingEnabled(
- eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(false);
- lenient()
- .when(IwlanHelper.isDefaultDataSlot(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
- .thenReturn(true);
+ NetworkCapabilities nc = prepareWifiNetworkCapabilitiesForTest();
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
+ mIwlanNetworkServiceProvider.subscriptionChanged();
+ // Create expected NetworkRegistrationInfo
+ NetworkRegistrationInfo.Builder expectedStateBuilder =
+ generateStateBuilder(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+
+ verify(mCallback, timeout(1000).times(1))
+ .onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS),
+ eq(expectedStateBuilder.build()));
+ }
+
+ @Test
+ public void testNetworkRegistrationInfoHomeForWiFiAndCstDisabled() throws Exception {
+ mIwlanNetworkServiceProvider = initNSP();
assertTrue(mIwlanNetworkServiceProvider != null);
- // Set Network on and verify mCallback should receive onNetworkStateChanged.
- mIwlanNetworkService.setNetworkConnected(true, IwlanNetworkService.Transport.WIFI);
- verify(mCallback, timeout(1000).times(1)).onNetworkStateChanged();
+ when(mMockImsMmTelManager.isCrossSimCallingEnabled()).thenReturn(false);
+
+ NetworkCapabilities nc = prepareWifiNetworkCapabilitiesForTest();
+ mIwlanNetworkService.getNetworkMonitorCallback().onCapabilitiesChanged(mMockNetwork, nc);
- // Set Sub active and verify mCallback should receive onNetworkStateChanged.
mIwlanNetworkServiceProvider.subscriptionChanged();
- verify(mCallback, timeout(1000).times(2)).onNetworkStateChanged();
// Create expected NetworkRegistrationInfo
NetworkRegistrationInfo.Builder expectedStateBuilder =
generateStateBuilder(
- domain, mIsSubActive, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ NetworkRegistrationInfo.DOMAIN_PS,
+ true /* isSubActive */,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- mBinder.requestNetworkRegistrationInfo(0, domain, mCallback);
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
verify(mCallback, timeout(1000).times(1))
.onRequestNetworkRegistrationInfoComplete(
eq(NetworkServiceCallback.RESULT_SUCCESS),
eq(expectedStateBuilder.build()));
-
- IwlanNetworkService.setNetworkConnected(
- false, IwlanNetworkService.Transport.UNSPECIFIED_NETWORK);
}
private NetworkRegistrationInfo.Builder generateStateBuilder(
diff --git a/test/com/google/android/iwlan/TunnelMetricsInterfaceTest.java b/test/com/google/android/iwlan/TunnelMetricsInterfaceTest.java
new file mode 100644
index 0000000..509037f
--- /dev/null
+++ b/test/com/google/android/iwlan/TunnelMetricsInterfaceTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.iwlan;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+
+import com.google.android.iwlan.TunnelMetricsInterface.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(JUnit4.class)
+public class TunnelMetricsInterfaceTest {
+ private static final String TEST_EPDG_ADDRESS = "127.0.0.1";
+ private static final String TEST_APN_NAME = "www.xyz.com";
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Test
+ public void testTunnelMetricsBuilder() {
+ TunnelMetricsData metricsData =
+ new TunnelMetricsData.Builder()
+ .setApnName(TEST_APN_NAME)
+ .setEpdgServerAddress(InetAddresses.parseNumericAddress(TEST_EPDG_ADDRESS))
+ .build();
+ assertEquals(TEST_APN_NAME, metricsData.getApnName());
+ assertEquals(TEST_EPDG_ADDRESS, metricsData.getEpdgServerAddress());
+ }
+
+ @Test
+ public void testOnOpenedMetricsBuilder() {
+ OnOpenedMetrics metricsData =
+ new OnOpenedMetrics.Builder()
+ .setApnName(TEST_APN_NAME)
+ .setEpdgServerAddress(InetAddresses.parseNumericAddress(TEST_EPDG_ADDRESS))
+ .build();
+ assertEquals(TEST_APN_NAME, metricsData.getApnName());
+ assertEquals(TEST_EPDG_ADDRESS, metricsData.getEpdgServerAddress());
+ }
+
+ @Test
+ public void testOnClosedMetricsBuilder() {
+ OnClosedMetrics metricsData =
+ new OnClosedMetrics.Builder()
+ .setApnName(TEST_APN_NAME)
+ .setEpdgServerAddress(InetAddresses.parseNumericAddress(TEST_EPDG_ADDRESS))
+ .build();
+ assertEquals(TEST_APN_NAME, metricsData.getApnName());
+ assertEquals(TEST_EPDG_ADDRESS, metricsData.getEpdgServerAddress());
+ }
+}
diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
index 9a4af93..3f23dab 100644
--- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
@@ -44,11 +44,13 @@ import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoNr;
import android.telephony.CellInfoWcdma;
+import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.google.android.iwlan.ErrorPolicyManager;
import com.google.android.iwlan.IwlanError;
import org.junit.After;
@@ -75,6 +77,11 @@ public class EpdgSelectorTest {
private static final String TEST_IP_ADDRESS = "127.0.0.1";
private static final String TEST_IP_ADDRESS_1 = "127.0.0.2";
private static final String TEST_IP_ADDRESS_2 = "127.0.0.3";
+ 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;
@@ -86,6 +93,7 @@ public class EpdgSelectorTest {
@Mock private Context mMockContext;
@Mock private Network mMockNetwork;
+ @Mock private ErrorPolicyManager mMockErrorPolicyManager;
@Mock private SubscriptionManager mMockSubscriptionManager;
@Mock private SubscriptionInfo mMockSubscriptionInfo;
@Mock private CarrierConfigManager mMockCarrierConfigManager;
@@ -108,9 +116,15 @@ public class EpdgSelectorTest {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mStaticMockSession = mockitoSession().mockStatic(DnsResolver.class).startMocking();
+ mStaticMockSession =
+ mockitoSession()
+ .mockStatic(DnsResolver.class)
+ .mockStatic(ErrorPolicyManager.class)
+ .startMocking();
- mEpdgSelector = new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX);
+ when(ErrorPolicyManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX))
+ .thenReturn(mMockErrorPolicyManager);
+ mEpdgSelector = spy(new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX));
when(mMockContext.getSystemService(eq(SubscriptionManager.class)))
.thenReturn(mMockSubscriptionManager);
@@ -122,6 +136,8 @@ public class EpdgSelectorTest {
when(mMockSubscriptionInfo.getMncString()).thenReturn("120");
+ when(mMockTelephonyManager.getNetworkOperator()).thenReturn("311120");
+
when(mMockContext.getSystemService(eq(TelephonyManager.class)))
.thenReturn(mMockTelephonyManager);
@@ -140,12 +156,13 @@ public class EpdgSelectorTest {
// Mock carrier configs with test bundle
mTestBundle = new PersistableBundle();
+ mTestBundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_PREFERRED);
when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
.thenReturn(mMockCarrierConfigManager);
when(mMockCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mTestBundle);
- lenient().when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver);
-
mFakeDns = new FakeDns();
mFakeDns.startMocking();
}
@@ -158,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);
@@ -174,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);
@@ -197,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
@@ -213,58 +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 expectedFqdn1 =
- (isEmergency)
- ? "sos.epdg.epc.mnc480.mcc310.pub.3gppnetwork.org"
- : "epdg.epc.mnc480.mcc310.pub.3gppnetwork.org";
- String expectedFqdn2 =
- (isEmergency)
- ? "sos.epdg.epc.mnc120.mcc300.pub.3gppnetwork.org"
- : "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org";
- String expectedFqdn3 =
- (isEmergency)
- ? "sos.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org"
- : "epdg.epc.mnc120.mcc311.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"});
-
- mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
- mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
- mFakeDns.setAnswer(expectedFqdn3, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+ new String[] {"310-480", "300-120", "311-120", "311-121"});
+
+ 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." + expectedFqdnFromEhplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A);
+ mFakeDns.setAnswer(
+ "sos." + excludedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, TYPE_A);
+ mFakeDns.setAnswer(expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_6}, TYPE_A);
+ mFakeDns.setAnswer(
+ "sos." + expectedFqdnFromRplmn, new String[] {TEST_IP_ADDRESS_7}, TYPE_A);
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_2));
- assertEquals(testInetAddresses.get(2), InetAddress.getByName(TEST_IP_ADDRESS_1));
+ if (isEmergency) {
+ assertEquals(6, testInetAddresses.size());
+ 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_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";
@@ -284,27 +542,37 @@ 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)
throws Exception {
+ return getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4V6, EpdgSelector.IPV4_PREFERRED, isEmergency);
+ }
+
+ private ArrayList<InetAddress> getValidatedServerListWithIpPreference(
+ @EpdgSelector.ProtoFilter int filter,
+ @EpdgSelector.EpdgAddressOrder int order,
+ boolean isEmergency)
+ throws Exception {
ArrayList<InetAddress> testInetAddresses = new ArrayList<InetAddress>();
final CountDownLatch latch = new CountDownLatch(1);
IwlanError ret =
mEpdgSelector.getValidatedServerList(
1234,
- EpdgSelector.PROTO_FILTER_IPV4V6,
- false /*isRoaming*/,
+ filter,
+ order,
+ false /* isRoaming */,
isEmergency,
mMockNetwork,
new EpdgSelector.EpdgSelectorCallback() {
@Override
public void onServerListChanged(
- int transactionId, ArrayList<InetAddress> validIPList) {
+ int transactionId, List<InetAddress> validIPList) {
assertEquals(transactionId, 1234);
for (InetAddress mInetAddress : validIPList) {
@@ -357,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)));
}
@@ -378,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";
@@ -429,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)
@@ -471,6 +741,197 @@ public class EpdgSelectorTest {
mFakeDns.setAnswer(expectedFqdn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
}
+ @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;
+
+ mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(addr2, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+
+ // Set carrier config mock
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC});
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, testStaticAddress);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4V6,
+ EpdgSelector.IPV4_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(2, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0));
+ assertEquals(InetAddress.getByName(TEST_IPV6_ADDRESS), testInetAddresses.get(1));
+ }
+
+ @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;
+
+ mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(addr2, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+
+ // Set carrier config mock
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC});
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, testStaticAddress);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4V6,
+ EpdgSelector.IPV6_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(2, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IPV6_ADDRESS), testInetAddresses.get(0));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(1));
+ }
+
+ @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;
+
+ mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(addr2, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+
+ // Set carrier config mock
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC});
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, testStaticAddress);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4,
+ EpdgSelector.SYSTEM_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(1, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0));
+ }
+
+ @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);
+
+ 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";
+
+ 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"});
+
+ mFakeDns.setAnswer(expectedFqdnFromHplmn, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+ mFakeDns.setAnswer(expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4,
+ EpdgSelector.SYSTEM_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(1, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0));
+ }
+
+ @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;
+
+ mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(addr2, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+
+ // Set carrier config mock
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC});
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, testStaticAddress);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV6,
+ EpdgSelector.SYSTEM_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(1, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IPV6_ADDRESS), testInetAddresses.get(0));
+ }
+
+ @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";
+ final String testStaticAddress = addr1 + "," + addr2 + "," + addr3;
+
+ mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(addr2, new String[] {TEST_IPV6_ADDRESS}, TYPE_AAAA);
+ mFakeDns.setAnswer(addr3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
+
+ // Set carrier config mock
+ mTestBundle.putIntArray(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+ new int[] {CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC});
+ mTestBundle.putString(
+ CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING, testStaticAddress);
+
+ ArrayList<InetAddress> testInetAddresses =
+ getValidatedServerListWithIpPreference(
+ EpdgSelector.PROTO_FILTER_IPV4V6,
+ EpdgSelector.SYSTEM_PREFERRED,
+ false /*isEmergency*/);
+
+ assertEquals(3, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(0));
+ assertEquals(InetAddress.getByName(TEST_IPV6_ADDRESS), testInetAddresses.get(1));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2));
+ }
+
/**
* Fakes DNS responses.
*
@@ -492,24 +953,24 @@ public class EpdgSelectorTest {
// Full match or partial match that target host contains the entry hostname to support
// random private dns probe hostname.
private boolean matches(String hostname, int type) {
- return hostname.endsWith(mHostname) && type == mType;
+ return hostname.equals(mHostname) && type == mType;
}
}
- 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. */
@@ -530,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;
}
@@ -545,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 f2cba73..76ceec8 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -19,11 +19,23 @@ package com.google.android.iwlan.epdg;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+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;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,6 +59,7 @@ import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.SaProposal;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeIOException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.ipsec.ike.ike3gpp.Ike3gppBackoffTimer;
@@ -61,8 +74,12 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import com.google.android.iwlan.IwlanError;
+import com.google.android.iwlan.IwlanTunnelMetricsImpl;
+import com.google.android.iwlan.TunnelMetricsInterface.OnClosedMetrics;
+import com.google.android.iwlan.TunnelMetricsInterface.OnOpenedMetrics;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,7 +95,6 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -86,41 +102,30 @@ import java.util.concurrent.Executor;
public class EpdgTunnelManagerTest {
public static final int DEFAULT_SLOT_INDEX = 0;
public static final int DEFAULT_SUBID = 0;
+ 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<>(
- Arrays.asList(
- new InetAddress[] {InetAddresses.parseNumericAddress("201.1.100.10")}));
- private static final ArrayList<InetAddress> EXPECTED_EPDG_ADDRESSES =
- new ArrayList<>(
- Arrays.asList(
- new InetAddress[] {InetAddresses.parseNumericAddress(EPDG_ADDRESS)}));
- private static final ArrayList<LinkAddress> EXPECTED_INTERNAL_ADDRESSES =
- new ArrayList<>(
- Arrays.asList(
- new LinkAddress[] {
- new LinkAddress(
- InetAddresses.parseNumericAddress("198.50.100.10"), 24)
- }));
- private static final ArrayList<InetAddress> EXPECTED_PCSCF_ADDRESSES =
- new ArrayList<>(
- Arrays.asList(
- new InetAddress[] {
- InetAddresses.parseNumericAddress("198.51.100.10")
- }));
- private static final ArrayList<InetAddress> EXPECTED_DNS_ADDRESSES =
- new ArrayList<>(
- Arrays.asList(
- new InetAddress[] {
- 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;
- private class IwlanTunnelCallback implements EpdgTunnelManager.TunnelCallback {
+ private static class IwlanTunnelCallback implements EpdgTunnelManager.TunnelCallback {
public void onOpened(String apnName, TunnelLinkProperties linkProperties) {}
public void onClosed(String apnName, IwlanError error) {}
@@ -130,10 +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;
@@ -142,6 +148,7 @@ public class EpdgTunnelManagerTest {
@Mock IpSecManager mMockIpSecManager;
@Mock EpdgTunnelManager.IkeSessionCreator mMockIkeSessionCreator;
@Mock IkeException mMockIkeException;
+ @Mock IkeIOException mMockIkeIoException;
@Mock IkeSessionConfiguration mMockIkeSessionConfiguration;
@Mock ChildSessionConfiguration mMockChildSessionConfiguration;
@Mock IpSecManager.IpSecTunnelInterface mMockIpSecTunnelInterface;
@@ -150,10 +157,20 @@ public class EpdgTunnelManagerTest {
@Mock IpSecTransform mMockedIpSecTransformOut;
@Mock LinkProperties mMockLinkProperties;
- ArgumentCaptor<ChildSessionCallback> mChildSessionCallbackCaptor;
+ static class IkeSessionArgumentCaptors {
+ ArgumentCaptor<IkeSessionParams> mIkeSessionParamsCaptor =
+ ArgumentCaptor.forClass(IkeSessionParams.class);
+ ArgumentCaptor<ChildSessionParams> mChildSessionParamsCaptor =
+ ArgumentCaptor.forClass(ChildSessionParams.class);
+ ArgumentCaptor<IkeSessionCallback> mIkeSessionCallbackCaptor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ ArgumentCaptor<ChildSessionCallback> mChildSessionCallbackCaptor =
+ ArgumentCaptor.forClass(ChildSessionCallback.class);
+ }
@Before
public void setUp() throws Exception {
+ EpdgTunnelManager.resetAllInstances();
mEpdgTunnelManager = spy(EpdgTunnelManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX));
when(mMockContext.getSystemService(eq(IpSecManager.class))).thenReturn(mMockIpSecManager);
@@ -161,16 +178,16 @@ public class EpdgTunnelManagerTest {
doReturn(mTestLooper.getLooper()).when(mEpdgTunnelManager).getLooper();
setVariable(mEpdgTunnelManager, "mContext", mMockContext);
mEpdgTunnelManager.initHandler();
- mEpdgTunnelManager.resetTunnelManagerState();
- when(mEpdgTunnelManager.getEpdgSelector()).thenReturn(mMockEpdgSelector);
+ doReturn(mMockEpdgSelector).when(mEpdgTunnelManager).getEpdgSelector();
when(mEpdgTunnelManager.getIkeSessionCreator()).thenReturn(mMockIkeSessionCreator);
when(mMockEpdgSelector.getValidatedServerList(
anyInt(),
anyInt(),
+ anyInt(),
anyBoolean(),
anyBoolean(),
- eq(mMockNetwork),
+ any(Network.class),
any(EpdgSelector.EpdgSelectorCallback.class)))
.thenReturn(new IwlanError(IwlanError.NO_ERROR));
@@ -185,18 +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);
-
- mChildSessionCallbackCaptor = ArgumentCaptor.forClass(ChildSessionCallback.class);
+ 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
@@ -204,7 +223,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_PPP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertFalse(ret);
}
@@ -213,13 +233,15 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IPV6, 16),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertFalse(ret);
ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IPV6, -1),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertFalse(ret);
}
@@ -242,13 +264,19 @@ public class EpdgTunnelManagerTest {
doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName2));
doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName3));
- boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR_v4, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR_v4, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
- ret = mEpdgTunnelManager.bringUpTunnel(TSR_v6, mMockIwlanTunnelCallback);
+ ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR_v6, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
- ret = mEpdgTunnelManager.bringUpTunnel(TSR_v4v6, mMockIwlanTunnelCallback);
+ ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR_v4v6, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
}
@@ -259,7 +287,9 @@ public class EpdgTunnelManagerTest {
when(mEpdgTunnelManager.getTunnelSetupRequestApnName(TSR)).thenReturn(null);
- boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertFalse(ret);
verify(mEpdgTunnelManager).getTunnelSetupRequestApnName(TSR);
}
@@ -270,7 +300,9 @@ public class EpdgTunnelManagerTest {
when(mEpdgTunnelManager.isTunnelConfigContainExistApn(TEST_APN_NAME)).thenReturn(true);
- boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertFalse(ret);
verify(mEpdgTunnelManager).isTunnelConfigContainExistApn(TEST_APN_NAME);
}
@@ -282,10 +314,17 @@ public class EpdgTunnelManagerTest {
TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName2, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
+ testApnName2,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
- boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
}
@@ -298,21 +337,25 @@ public class EpdgTunnelManagerTest {
PersistableBundle bundle = new PersistableBundle();
setupMockForGetConfig(bundle);
- boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
verify(mMockEpdgSelector)
.getValidatedServerList(
anyInt(),
- eq(EpdgSelector.PROTO_FILTER_IPV4),
+ eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ anyInt(),
eq(false),
eq(false),
- eq(mMockNetwork),
+ eq(mMockDefaultNetwork),
any());
}
- private void setupTunnelBringup() throws Exception {
+ private void setupTunnelBringup(
+ String apnName, List<InetAddress> epdgAddresses, int transactionId) throws Exception {
setupMockForGetConfig(null);
doReturn(null)
.when(mMockIkeSessionCreator)
@@ -324,21 +367,27 @@ public class EpdgTunnelManagerTest {
any(IkeSessionCallback.class),
any(ChildSessionCallback.class));
- doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(apnName));
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
- getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ getBasicTunnelSetupRequest(apnName, ApnSetting.PROTOCOL_IP),
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
mEpdgTunnelManager.sendSelectionRequestComplete(
- EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1);
+ epdgAddresses, new IwlanError(IwlanError.NO_ERROR), transactionId);
mTestLooper.dispatchAll();
}
+ private void setupTunnelBringup() throws Exception {
+ setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES, 1 /* transactionId */);
+ }
+
@Test
+ @Ignore("b/239753287- Telus carrier errors out on parsing DEVICE_IDENTITY response")
public void testBringUpTunnelSetsDeviceIdentityImeiSv() throws Exception {
when(mMockContext.getSystemService(eq(TelephonyManager.class)))
.thenReturn(mMockTelephonyManager);
@@ -370,6 +419,7 @@ public class EpdgTunnelManagerTest {
}
@Test
+ @Ignore("b/239753287- Telus carrier errors out on parsing DEVICE_IDENTITY response")
public void testBringUpTunnelSetsDeviceIdentityImei() throws Exception {
when(mMockContext.getSystemService(eq(TelephonyManager.class)))
.thenReturn(mMockTelephonyManager);
@@ -399,6 +449,7 @@ public class EpdgTunnelManagerTest {
}
@Test
+ @Ignore("b/239753287- Telus carrier errors out on parsing DEVICE_IDENTITY response")
public void testBringUpTunnelNoDeviceIdentityWhenImeiUnavailable() throws Exception {
when(mMockContext.getSystemService(eq(TelephonyManager.class)))
.thenReturn(mMockTelephonyManager);
@@ -426,29 +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);
- 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())
@@ -460,18 +507,57 @@ 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
+ public void testInitialContactForFirstTunnelOnly() throws Exception {
+ final String firstApnName = "ims";
+ final String secondApnName = "mms";
+
+ IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(firstApnName);
+ ChildSessionCallback firstCallback =
+ firstTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+
+ IkeSessionArgumentCaptors secondTunnelArgumentCaptors =
+ verifyBringUpTunnel(secondApnName, true /* needPendingBringUpReq */);
+ verifyTunnelOnOpened(firstApnName, firstCallback);
+
+ ChildSessionCallback secondCallback =
+ secondTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(secondApnName, secondCallback);
+
+ IkeSessionParams firstTunnelParams =
+ firstTunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue();
+ IkeSessionParams secondTunnelParams =
+ secondTunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue();
+ assertTrue(firstTunnelParams.hasIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT));
+ assertFalse(secondTunnelParams.hasIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT));
}
@Test
public void testCloseTunnelWithNoTunnelForApn() throws Exception {
String testApnName = "www.xyz.com";
+ doReturn(0L)
+ .when(mEpdgTunnelManager)
+ .reportIwlanError(eq(testApnName), eq(new IwlanError(IwlanError.TUNNEL_NOT_FOUND)));
- boolean ret = mEpdgTunnelManager.closeTunnel(testApnName, false /*forceClose*/);
- assertTrue(ret);
+ mEpdgTunnelManager.closeTunnel(
+ testApnName,
+ false /*forceClose*/,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
mTestLooper.dispatchAll();
+
verify(mEpdgTunnelManager).closePendingRequestsForApn(eq(testApnName));
+ verify(mMockIwlanTunnelCallback)
+ .onClosed(eq(testApnName), eq(new IwlanError(IwlanError.TUNNEL_NOT_FOUND)));
+ ArgumentCaptor<OnClosedMetrics> metricsCaptor =
+ ArgumentCaptor.forClass(OnClosedMetrics.class);
+ verify(mMockIwlanTunnelMetrics, times(1)).onClosed(metricsCaptor.capture());
+ assertEquals(testApnName, metricsCaptor.getValue().getApnName());
}
@Test
@@ -479,10 +565,18 @@ public class EpdgTunnelManagerTest {
String testApnName = "www.xyz.com";
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
-
- boolean ret = mEpdgTunnelManager.closeTunnel(testApnName, true /*forceClose*/);
- assertTrue(ret);
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+
+ mEpdgTunnelManager.closeTunnel(
+ testApnName,
+ true /*forceClose*/,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
mTestLooper.dispatchAll();
verify(mMockIkeSession).kill();
@@ -494,10 +588,18 @@ public class EpdgTunnelManagerTest {
String testApnName = "www.xyz.com";
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
-
- boolean ret = mEpdgTunnelManager.closeTunnel(testApnName, false /*forceClose*/);
- assertTrue(ret);
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+
+ mEpdgTunnelManager.closeTunnel(
+ testApnName,
+ false /*forceClose*/,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
mTestLooper.dispatchAll();
verify(mMockIkeSession).close();
@@ -526,15 +628,6 @@ public class EpdgTunnelManagerTest {
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
-
doReturn(null)
.when(mMockIkeSessionCreator)
.createIkeSession(
@@ -549,7 +642,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -592,15 +686,6 @@ public class EpdgTunnelManagerTest {
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
-
doReturn(null)
.when(mMockIkeSessionCreator)
.createIkeSession(
@@ -615,7 +700,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -649,14 +735,6 @@ public class EpdgTunnelManagerTest {
bundle.putInt(CarrierConfigManager.Iwlan.KEY_DPD_TIMER_SEC_INT, testDpdDelay);
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
doReturn(null)
.when(mMockIkeSessionCreator)
@@ -672,7 +750,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -707,14 +786,6 @@ public class EpdgTunnelManagerTest {
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
setupMockForGetConfig(null);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
doReturn(null)
.doReturn(null)
@@ -731,7 +802,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -743,7 +815,7 @@ public class EpdgTunnelManagerTest {
EpdgTunnelManager.TmIkeSessionCallback ikeSessionCallback =
verifyCreateIkeSession(ipList2.get(0));
- ikeSessionCallback.onClosedExceptionally(
+ ikeSessionCallback.onClosedWithException(
new IkeInternalException(new IOException("Retransmitting failure")));
mTestLooper.dispatchAll();
@@ -764,14 +836,6 @@ public class EpdgTunnelManagerTest {
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
setupMockForGetConfig(null);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
doReturn(null)
.doReturn(null)
@@ -788,7 +852,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -801,7 +866,7 @@ public class EpdgTunnelManagerTest {
EpdgTunnelManager.TmIkeSessionCallback ikeSessionCallback =
verifyCreateIkeSession(ipList2.get(1));
- ikeSessionCallback.onClosedExceptionally(
+ ikeSessionCallback.onClosedWithException(
new IkeInternalException(new IOException("Retransmitting failure")));
mTestLooper.dispatchAll();
@@ -834,7 +899,7 @@ public class EpdgTunnelManagerTest {
LinkAddress l1 = new LinkAddress(a1, 64);
InetAddress src = InetAddress.getByName("2600:381:4872:5d1e:0:10:3582:a501");
EpdgTunnelManager.TunnelConfig tf =
- mEpdgTunnelManager.new TunnelConfig(null, null, src, 64);
+ mEpdgTunnelManager.new TunnelConfig(null, null, null, src, 64);
assertTrue(tf.isPrefixSameAsSrcIP(l1));
// different prefix length
@@ -888,14 +953,7 @@ public class EpdgTunnelManagerTest {
}
setupMockForGetConfig(null);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
+
doReturn(null)
.doReturn(null)
.when(mMockIkeSessionCreator)
@@ -911,7 +969,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -941,7 +1000,7 @@ public class EpdgTunnelManagerTest {
ike3gppCallback.onIke3gppDataReceived(ike3gppInfoList);
EpdgTunnelManager.TmIkeSessionCallback ikeSessionCallback =
ikeSessionCallbackCaptor.getValue();
- ikeSessionCallback.onClosedExceptionally(new IkeInternalException(new Exception()));
+ ikeSessionCallback.onClosedWithException(new IkeInternalException(new Exception()));
mTestLooper.dispatchAll();
// if expected backoff time is negative - verify that backoff time is not reported.
@@ -964,7 +1023,6 @@ public class EpdgTunnelManagerTest {
TunnelSetupRequest ret =
TunnelSetupRequest.builder()
.setApnName(apnName)
- .setNetwork(mMockNetwork)
.setIsRoaming(false /*isRoaming*/)
.setIsEmergency(false /*IsEmergency*/)
.setRequestPcscf(false /*requestPcscf*/)
@@ -977,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*/)
@@ -1014,6 +1071,12 @@ public class EpdgTunnelManagerTest {
SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
});
+ if (!bundle.containsKey(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT)) {
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_PREFERRED);
+ }
when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
.thenReturn(mMockCarrierConfigManager);
when(mMockContext.getSystemService(eq(ConnectivityManager.class)))
@@ -1036,28 +1099,36 @@ public class EpdgTunnelManagerTest {
}
@Test
- public void testHandleOnClosedWithEpdgAddressSelected_True() throws Exception {
+ public void testHandleOnClosedWithEpdgConnected_True() throws Exception {
String testApnName = "www.xyz.com";
- IwlanError error = new IwlanError(IwlanError.NETWORK_FAILURE);
+ IwlanError error =
+ new IwlanError(IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED);
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
-
- mEpdgTunnelManager.setIsEpdgAddressSelected(true);
-
- mEpdgTunnelManager.getTmIkeSessionCallback(testApnName).onClosed();
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+ int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName);
+
+ 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));
}
@Test
- public void testHandleOnClosedWithEpdgAddressSelected_False() throws Exception {
+ public void testHandleOnClosedWithEpdgConnected_False() throws Exception {
String testApnName = "www.xyz.com";
- IwlanError error = new IwlanError(IwlanError.NETWORK_FAILURE);
+ IwlanError error =
+ new IwlanError(IwlanError.IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED);
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
@@ -1068,7 +1139,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1076,27 +1148,35 @@ public class EpdgTunnelManagerTest {
EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1);
mTestLooper.dispatchAll();
- mEpdgTunnelManager.setIsEpdgAddressSelected(false);
+ mEpdgTunnelManager.onConnectedToEpdg(false);
- mEpdgTunnelManager.getTmIkeSessionCallback(testApnName).onClosed();
+ 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));
}
private void setOneTunnelOpened(String apnName) throws Exception {
mEpdgTunnelManager.putApnNameToTunnelConfig(
- apnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
- setVariable(mEpdgTunnelManager, "mLocalAddresses", EXPECTED_LOCAL_ADDRESSES);
+ apnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
mEpdgTunnelManager.validateAndSetEpdgAddress(EXPECTED_EPDG_ADDRESSES);
- mEpdgTunnelManager.setIsEpdgAddressSelected(true);
+ mEpdgTunnelManager.onConnectedToEpdg(true);
+ }
+
+ private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(String apnName) {
+ return verifyBringUpTunnelWithDnsQuery(apnName, null);
}
- private ChildSessionCallback verifyBringUpTunnelWithDnsQuery(
+ private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(
String apnName, IkeSession ikeSession) {
reset(mMockIwlanTunnelCallback);
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors = new IkeSessionArgumentCaptors();
verifyBringUpTunnel(apnName, true /* needPendingBringUpReq */);
@@ -1104,11 +1184,11 @@ public class EpdgTunnelManagerTest {
.when(mMockIkeSessionCreator)
.createIkeSession(
eq(mMockContext),
- any(IkeSessionParams.class),
- any(ChildSessionParams.class),
+ ikeSessionArgumentCaptors.mIkeSessionParamsCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionParamsCaptor.capture(),
any(Executor.class),
- any(IkeSessionCallback.class),
- mChildSessionCallbackCaptor.capture());
+ ikeSessionArgumentCaptors.mIkeSessionCallbackCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.capture());
mEpdgTunnelManager.sendSelectionRequestComplete(
EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1);
@@ -1117,56 +1197,62 @@ public class EpdgTunnelManagerTest {
verify(mMockIkeSessionCreator, times(1))
.createIkeSession(
eq(mMockContext),
- any(IkeSessionParams.class),
- any(ChildSessionParams.class),
+ ikeSessionArgumentCaptors.mIkeSessionParamsCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionParamsCaptor.capture(),
any(Executor.class),
- any(IkeSessionCallback.class),
- mChildSessionCallbackCaptor.capture());
+ ikeSessionArgumentCaptors.mIkeSessionCallbackCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.capture());
- return mChildSessionCallbackCaptor.getValue();
+ return ikeSessionArgumentCaptors;
}
- private ChildSessionCallback verifyBringUpTunnel(
+ private IkeSessionArgumentCaptors verifyBringUpTunnel(
String apnName, boolean needPendingBringUpReq) {
reset(mMockIkeSessionCreator);
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors = new IkeSessionArgumentCaptors();
doReturn(null)
.when(mMockIkeSessionCreator)
.createIkeSession(
eq(mMockContext),
- any(IkeSessionParams.class),
- any(ChildSessionParams.class),
+ ikeSessionArgumentCaptors.mIkeSessionParamsCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionParamsCaptor.capture(),
any(Executor.class),
- any(IkeSessionCallback.class),
- mChildSessionCallbackCaptor.capture());
+ ikeSessionArgumentCaptors.mIkeSessionCallbackCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.capture());
doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(apnName));
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(apnName, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
verify(mMockIkeSessionCreator, times(needPendingBringUpReq ? 0 : 1))
.createIkeSession(
eq(mMockContext),
- any(IkeSessionParams.class),
- any(ChildSessionParams.class),
+ ikeSessionArgumentCaptors.mIkeSessionParamsCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionParamsCaptor.capture(),
any(Executor.class),
- any(IkeSessionCallback.class),
- mChildSessionCallbackCaptor.capture());
+ ikeSessionArgumentCaptors.mIkeSessionCallbackCaptor.capture(),
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.capture());
- return needPendingBringUpReq ? null : mChildSessionCallbackCaptor.getValue();
+ return ikeSessionArgumentCaptors;
}
- private void verifyTunnelOnOpened(String apnName) {
- verifyTunnelOnOpened(apnName, mChildSessionCallbackCaptor.getValue());
- }
+ private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback)
+ throws Exception {
+ clearInvocations(mMockIpSecManager);
+ doReturn(0L)
+ .when(mEpdgTunnelManager)
+ .reportIwlanError(eq(apnName), eq(new IwlanError(IwlanError.NO_ERROR)));
- private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback) {
- mEpdgTunnelManager.getTmIkeSessionCallback(apnName).onOpened(mMockIkeSessionConfiguration);
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(apnName, mEpdgTunnelManager.getCurrentTokenForApn(apnName))
+ .onOpened(mMockIkeSessionConfiguration);
mTestLooper.dispatchAll();
childSessionCallback.onIpSecTransformCreated(
mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN);
@@ -1174,21 +1260,35 @@ 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());
}
@Test
- public void testHandleOnOpenedWithEpdgAddressSelected_True() throws Exception {
+ public void testHandleOnOpenedWithEpdgConnected_True() throws Exception {
final String openedApnName = "ims";
final String toBeOpenedApnName = "mms";
setOneTunnelOpened(openedApnName);
- verifyBringUpTunnel(toBeOpenedApnName, false /* needPendingBringUpReq */);
- verifyTunnelOnOpened(toBeOpenedApnName);
+ // 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 =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(toBeOpenedApnName, childSessionCallback);
}
@Test
@@ -1196,36 +1296,50 @@ public class EpdgTunnelManagerTest {
final String firstApnName = "ims";
final String secondApnName = "mms";
- ChildSessionCallback firstCallback = verifyBringUpTunnelWithDnsQuery(firstApnName, null);
- verifyBringUpTunnel(secondApnName, true /* needPendingBringUpReq */);
+ IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(firstApnName);
+ ChildSessionCallback firstCallback =
+ firstTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+
+ IkeSessionArgumentCaptors secondTunnelArgumentCaptors =
+ verifyBringUpTunnel(secondApnName, true /* needPendingBringUpReq */);
verifyTunnelOnOpened(firstApnName, firstCallback);
- verifyTunnelOnOpened(secondApnName, mChildSessionCallbackCaptor.getValue());
+
+ ChildSessionCallback secondCallback =
+ secondTunnelArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(secondApnName, secondCallback);
}
@Test
- public void testHandleOnClosedExceptionallyWithEpdgAddressSelected_True() throws Exception {
+ public void testHandleOnClosedExceptionallyWithEpdgConnected_True() throws Exception {
String testApnName = "www.xyz.com";
IwlanError error = new IwlanError(mMockIkeException);
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+ int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName);
- mEpdgTunnelManager.setIsEpdgAddressSelected(true);
+ mEpdgTunnelManager.onConnectedToEpdg(true);
+ mEpdgTunnelManager.setEpdgAddress(InetAddresses.parseNumericAddress(EPDG_ADDRESS));
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
- .onClosedExceptionally(mMockIkeException);
+ .getTmIkeSessionCallback(testApnName, token)
+ .onClosedWithException(mMockIkeException);
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));
}
@Test
- public void testHandleOnClosedExceptionallyWithEpdgAddressSelected_False() throws Exception {
+ public void testHandleOnClosedExceptionallyWithEpdgConnected_False() throws Exception {
String testApnName = "www.xyz.com";
IwlanError error = new IwlanError(mMockIkeException);
@@ -1238,7 +1352,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1246,15 +1361,14 @@ public class EpdgTunnelManagerTest {
EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1);
mTestLooper.dispatchAll();
- mEpdgTunnelManager.setIsEpdgAddressSelected(false);
+ mEpdgTunnelManager.onConnectedToEpdg(false);
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
- .onClosedExceptionally(mMockIkeException);
+ .getTmIkeSessionCallback(testApnName, DEFAULT_TOKEN)
+ .onClosedWithException(mMockIkeException);
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));
}
@@ -1265,12 +1379,18 @@ public class EpdgTunnelManagerTest {
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+ int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName);
when(mMockIkeSessionConfiguration.getPcscfServers()).thenReturn(EXPECTED_EPDG_ADDRESSES);
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
+ .getTmIkeSessionCallback(testApnName, token)
.onOpened(mMockIkeSessionConfiguration);
mTestLooper.dispatchAll();
@@ -1286,8 +1406,10 @@ public class EpdgTunnelManagerTest {
doThrow(new IllegalArgumentException())
.when(mMockIpSecManager)
.applyTunnelModeTransform(eq(mMockIpSecTunnelInterface), anyInt(), any());
- ChildSessionCallback childSessionCallback =
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
verifyBringUpTunnelWithDnsQuery(testApnName, mMockIkeSession);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
childSessionCallback.onIpSecTransformCreated(
mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN);
mTestLooper.dispatchAll();
@@ -1300,17 +1422,19 @@ public class EpdgTunnelManagerTest {
String testApnName = "ims";
when(mMockConnectivityManager.getLinkProperties(any())).thenReturn(mMockLinkProperties);
- ChildSessionCallback childSessionCallback =
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
verifyBringUpTunnelWithDnsQuery(testApnName, mMockIkeSession);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
childSessionCallback.onIpSecTransformCreated(
mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN);
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
+ .getTmIkeSessionCallback(testApnName, DEFAULT_TOKEN)
.onIkeSessionConnectionInfoChanged(mMockIkeSessionConnectionInfo);
mTestLooper.dispatchAll();
- verify(mMockIpSecTunnelInterface, times(1)).setUnderlyingNetwork(mMockNetwork);
+ verify(mMockIpSecTunnelInterface, times(1)).setUnderlyingNetwork(mMockDefaultNetwork);
}
@Test
@@ -1319,13 +1443,15 @@ public class EpdgTunnelManagerTest {
String testApnName = "ims";
when(mMockConnectivityManager.getLinkProperties(any())).thenReturn(null);
- ChildSessionCallback childSessionCallback =
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
verifyBringUpTunnelWithDnsQuery(testApnName, mMockIkeSession);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
childSessionCallback.onIpSecTransformCreated(
mMockedIpSecTransformIn, IpSecManager.DIRECTION_IN);
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
+ .getTmIkeSessionCallback(testApnName, DEFAULT_TOKEN)
.onIkeSessionConnectionInfoChanged(mMockIkeSessionConnectionInfo);
mTestLooper.dispatchAll();
@@ -1368,15 +1494,6 @@ public class EpdgTunnelManagerTest {
PersistableBundle bundle = new PersistableBundle();
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
-
doReturn(null)
.when(mMockIkeSessionCreator)
.createIkeSession(
@@ -1394,12 +1511,14 @@ public class EpdgTunnelManagerTest {
ret =
mEpdgTunnelManager.bringUpTunnel(
getHandoverTunnelSetupRequest(TEST_APN_NAME, apnProtocol),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
} else {
ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, apnProtocol),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
}
assertTrue(ret);
@@ -1499,17 +1618,23 @@ public class EpdgTunnelManagerTest {
doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
mEpdgTunnelManager.putApnNameToTunnelConfig(
- testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
+ testApnName,
+ mMockIkeSession,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics,
+ null,
+ 0);
+ int token = mEpdgTunnelManager.incrementAndGetCurrentTokenForApn(testApnName);
- mEpdgTunnelManager.setIsEpdgAddressSelected(true);
+ mEpdgTunnelManager.onConnectedToEpdg(true);
+ mEpdgTunnelManager.setEpdgAddress(InetAddresses.parseNumericAddress(EPDG_ADDRESS));
mEpdgTunnelManager
- .getTmIkeSessionCallback(testApnName)
- .onClosedExceptionally(mockException);
+ .getTmIkeSessionCallback(testApnName, token)
+ .onClosedWithException(mockException);
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));
}
@@ -1527,19 +1652,43 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
mEpdgTunnelManager.sendSelectionRequestComplete(null, error, 1);
mTestLooper.dispatchAll();
- mEpdgTunnelManager.setIsEpdgAddressSelected(false);
+ mEpdgTunnelManager.onConnectedToEpdg(false);
verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error));
}
@Test
+ public void testNeverReportIwlanErrorWhenCloseAnOpenedTunnel() throws Exception {
+ IkeInternalException ikeException =
+ new IkeInternalException(new IOException("Retransmitting failure"));
+
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME);
+
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback);
+
+ reset(mEpdgTunnelManager); // reset number of times of reportIwlanError()
+
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(TEST_APN_NAME, 0)
+ .onClosedWithException(ikeException);
+ mTestLooper.dispatchAll();
+ verify(mEpdgTunnelManager, never()).reportIwlanError(eq(TEST_APN_NAME), any());
+ verify(mMockIwlanTunnelCallback, times(1))
+ .onClosed(eq(TEST_APN_NAME), eq(new IwlanError(ikeException)));
+ }
+
+ @Test
public void testCanBringUpTunnel() throws Exception {
String testApnName = "www.xyz.com";
IwlanError error = new IwlanError(mMockIkeException);
@@ -1553,7 +1702,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
@@ -1567,15 +1717,6 @@ public class EpdgTunnelManagerTest {
PersistableBundle bundle = new PersistableBundle();
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
-
doReturn(null)
.when(mMockIkeSessionCreator)
.createIkeSession(
@@ -1593,7 +1734,8 @@ public class EpdgTunnelManagerTest {
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(
TEST_APN_NAME, ApnSetting.PROTOCOL_IPV6, PDU_SESSION_ID),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1637,14 +1779,6 @@ public class EpdgTunnelManagerTest {
bundle.putInt(CarrierConfigManager.Iwlan.KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, nattTimer);
setupMockForGetConfig(bundle);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(false),
- eq(false),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
doReturn(null)
.when(mMockIkeSessionCreator)
@@ -1660,7 +1794,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1698,7 +1833,6 @@ public class EpdgTunnelManagerTest {
TunnelSetupRequest tsr =
TunnelSetupRequest.builder()
.setApnName(testApnName)
- .setNetwork(mMockNetwork)
.setApnIpProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setSrcIpv6Address(testAddressV6)
.setSrcIpv6AddressPrefixLength(ipv6AddressLen)
@@ -1710,14 +1844,6 @@ public class EpdgTunnelManagerTest {
.build();
setupMockForGetConfig(null);
- when(mMockEpdgSelector.getValidatedServerList(
- anyInt(),
- anyInt(),
- eq(isRoaming),
- eq(isEmergency),
- eq(mMockNetwork),
- any(EpdgSelector.EpdgSelectorCallback.class)))
- .thenReturn(new IwlanError(IwlanError.NO_ERROR));
doReturn(null)
.when(mMockIkeSessionCreator)
@@ -1730,7 +1856,9 @@ public class EpdgTunnelManagerTest {
any(ChildSessionCallback.class));
doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName));
- boolean ret = mEpdgTunnelManager.bringUpTunnel(tsr, mMockIwlanTunnelCallback);
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ tsr, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1739,9 +1867,10 @@ public class EpdgTunnelManagerTest {
.getValidatedServerList(
anyInt(),
anyInt(), // only Ipv6 address is added
+ anyInt(),
eq(isRoaming),
eq(isEmergency),
- eq(mMockNetwork),
+ eq(mMockDefaultNetwork),
any(EpdgSelector.EpdgSelectorCallback.class));
mEpdgTunnelManager.sendSelectionRequestComplete(
@@ -1771,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
@@ -1835,7 +1964,8 @@ public class EpdgTunnelManagerTest {
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1859,12 +1989,14 @@ public class EpdgTunnelManagerTest {
when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(DEFAULT_SLOT_INDEX))
.thenReturn(mMockSubscriptionInfo)
+ .thenReturn(mMockSubscriptionInfo)
.thenReturn(null);
boolean ret =
mEpdgTunnelManager.bringUpTunnel(
getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
- mMockIwlanTunnelCallback);
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
assertTrue(ret);
mTestLooper.dispatchAll();
@@ -1874,4 +2006,474 @@ public class EpdgTunnelManagerTest {
verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
}
+
+ @Test
+ public void testCloseTunnelWithEpdgSelectionIncomplete() {
+ // Bring up tunnel
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+
+ // close tunnel when ePDG selection is incomplete
+ mEpdgTunnelManager.closeTunnel(
+ TEST_APN_NAME,
+ false /*forceClose*/,
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
+ mTestLooper.dispatchAll();
+
+ verify(mMockIwlanTunnelCallback, times(1))
+ .onClosed(eq(TEST_APN_NAME), eq(new IwlanError(IwlanError.NO_ERROR)));
+ ArgumentCaptor<OnClosedMetrics> metricsCaptor =
+ ArgumentCaptor.forClass(OnClosedMetrics.class);
+ verify(mMockIwlanTunnelMetrics, times(1)).onClosed(metricsCaptor.capture());
+ assertEquals(TEST_APN_NAME, metricsCaptor.getValue().getApnName());
+ assertNull(metricsCaptor.getValue().getEpdgServerAddress());
+ }
+
+ @Test
+ public void testIgnoreSignalFromObsoleteCallback() throws Exception {
+ int transactionId = 0;
+
+ // testApnName with token 0
+ 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));
+
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(TEST_APN_NAME, 0 /* token */)
+ .onClosedWithException(mMockIkeException);
+ mTestLooper.dispatchAll();
+ verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error));
+ assertNull(mEpdgTunnelManager.getTunnelConfigForApn(TEST_APN_NAME));
+
+ // testApnName1 with token 1
+ setupTunnelBringup(TEST_APN_NAME, EXPECTED_EPDG_ADDRESSES, ++transactionId);
+ mEpdgTunnelManager.onConnectedToEpdg(true);
+
+ // signal from obsolete callback (token 0), ignore it
+ reset(mMockIwlanTunnelCallback);
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(TEST_APN_NAME, 0 /* token */)
+ .onClosedWithException(mMockIkeException);
+ mTestLooper.dispatchAll();
+ verify(mMockIwlanTunnelCallback, never()).onClosed(eq(TEST_APN_NAME), eq(error));
+ assertNotNull(mEpdgTunnelManager.getTunnelConfigForApn(TEST_APN_NAME));
+
+ // signals from active callback
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(TEST_APN_NAME, 1 /* token */)
+ .onClosedWithException(mMockIkeException);
+ mTestLooper.dispatchAll();
+ verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(TEST_APN_NAME), eq(error));
+ assertNull(mEpdgTunnelManager.getTunnelConfigForApn(TEST_APN_NAME));
+ }
+
+ @Test
+ public void testBringUpTunnelIpv4Preferred() throws Exception {
+ TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_PREFERRED);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector)
+ .getValidatedServerList(
+ anyInt(),
+ eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ eq(EpdgSelector.IPV4_PREFERRED),
+ eq(false),
+ eq(false),
+ eq(mMockDefaultNetwork),
+ any());
+ }
+
+ @Test
+ public void testBringUpTunnelIpv6Preferred() throws Exception {
+ TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_PREFERRED);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector)
+ .getValidatedServerList(
+ anyInt(),
+ eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ eq(EpdgSelector.IPV6_PREFERRED),
+ eq(false),
+ eq(false),
+ eq(mMockDefaultNetwork),
+ any());
+ }
+
+ @Test
+ public void testBringUpTunnelIpv4Only() throws Exception {
+ TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV4_ONLY);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector)
+ .getValidatedServerList(
+ anyInt(),
+ eq(EpdgSelector.PROTO_FILTER_IPV4),
+ eq(EpdgSelector.SYSTEM_PREFERRED),
+ eq(false),
+ eq(false),
+ eq(mMockDefaultNetwork),
+ any());
+ }
+
+ @Test
+ public void testBringUpTunnelIpv6Only() throws Exception {
+ TunnelSetupRequest TSR =
+ getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IPV6);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+ doReturn(EXPECTED_IPV6_LOCAL_ADDRESSES)
+ .when(mEpdgTunnelManager)
+ .getAddressForNetwork(any(), any());
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_ONLY);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector)
+ .getValidatedServerList(
+ anyInt(),
+ eq(EpdgSelector.PROTO_FILTER_IPV6),
+ eq(EpdgSelector.SYSTEM_PREFERRED),
+ eq(false),
+ eq(false),
+ eq(mMockDefaultNetwork),
+ any());
+ }
+
+ @Test
+ public void testBringUpTunnelIpv6OnlyOnIpv4Wifi() throws Exception {
+ TunnelSetupRequest TSR =
+ getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IPV6);
+ IwlanError error = new IwlanError(IwlanError.EPDG_ADDRESS_ONLY_IPV6_ALLOWED);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+ doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(TEST_APN_NAME), eq(error));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_IPV6_ONLY);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector, never())
+ .getValidatedServerList(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(false),
+ eq(false),
+ 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));
+ }
+
+ @Test
+ public void testBringUpTunnelSystemPreferred() throws Exception {
+ TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(
+ CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT,
+ CarrierConfigManager.Iwlan.EPDG_ADDRESS_SYSTEM_PREFERRED);
+ setupMockForGetConfig(bundle);
+
+ boolean ret =
+ mEpdgTunnelManager.bringUpTunnel(
+ TSR, mMockIwlanTunnelCallback, mMockIwlanTunnelMetrics);
+ assertTrue(ret);
+ mTestLooper.dispatchAll();
+
+ verify(mMockEpdgSelector)
+ .getValidatedServerList(
+ anyInt(),
+ eq(EpdgSelector.PROTO_FILTER_IPV4V6),
+ eq(EpdgSelector.SYSTEM_PREFERRED),
+ eq(false),
+ eq(false),
+ eq(mMockDefaultNetwork),
+ any());
+ }
+
+ @Test
+ public void testOnOpenedTunnelMetricsData() throws Exception {
+ doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+ mEpdgTunnelManager.bringUpTunnel(
+ getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+ mMockIwlanTunnelCallback,
+ mMockIwlanTunnelMetrics);
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback);
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<OnOpenedMetrics> metricsCaptor =
+ ArgumentCaptor.forClass(OnOpenedMetrics.class);
+ verify(mMockIwlanTunnelMetrics, times(1)).onOpened(metricsCaptor.capture());
+ assertEquals(TEST_APN_NAME, metricsCaptor.getValue().getApnName());
+ }
+
+ @Test
+ public void testCloseTunnelWithIkeInitTimeout() throws Exception {
+ String testApnName = "www.xyz.com";
+ IwlanError error = new IwlanError(IwlanError.IKE_INIT_TIMEOUT, mMockIkeIoException);
+ doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
+
+ setupTunnelBringup();
+
+ ArgumentCaptor<EpdgTunnelManager.TmIkeSessionCallback> ikeSessionCallbackCaptor =
+ ArgumentCaptor.forClass(EpdgTunnelManager.TmIkeSessionCallback.class);
+ verify(mMockIkeSessionCreator, atLeastOnce())
+ .createIkeSession(
+ eq(mMockContext),
+ any(IkeSessionParams.class),
+ any(ChildSessionParams.class),
+ any(Executor.class),
+ ikeSessionCallbackCaptor.capture(),
+ any(ChildSessionCallback.class));
+ ikeSessionCallbackCaptor.getValue().onClosedWithException(mMockIkeIoException);
+ mTestLooper.dispatchAll();
+
+ verify(mEpdgTunnelManager, times(1)).reportIwlanError(eq(testApnName), eq(error));
+ verify(mMockIwlanTunnelCallback, atLeastOnce()).onClosed(eq(testApnName), eq(error));
+ }
+
+ @Test
+ public void testCloseTunnelWithIkeDpdTimeout() throws Exception {
+ IwlanError error = new IwlanError(IwlanError.IKE_DPD_TIMEOUT, mMockIkeIoException);
+
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback);
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(
+ TEST_APN_NAME, mEpdgTunnelManager.getCurrentTokenForApn(TEST_APN_NAME))
+ .onClosedWithException(mMockIkeIoException);
+ mTestLooper.dispatchAll();
+
+ 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);
+
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(TEST_APN_NAME, mMockIkeSession);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(TEST_APN_NAME, childSessionCallback);
+
+ Network newNetwork = mock(Network.class);
+ mEpdgTunnelManager.updateNetwork(newNetwork, mMockLinkProperties);
+ mTestLooper.dispatchAll();
+
+ mEpdgTunnelManager
+ .getTmIkeSessionCallback(
+ TEST_APN_NAME, mEpdgTunnelManager.getCurrentTokenForApn(TEST_APN_NAME))
+ .onClosedWithException(mMockIkeIoException);
+ mTestLooper.dispatchAll();
+
+ 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));
+ }
}