Merge a7cabd5d16264bb5c64c99240d7fc87cb6aa04d4 on remote branch
Change-Id: I56e5edfa2acd33cc61b000eb48d6362bc3f6a15e
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6a7a2d4..5c3cfac 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,6 +8,7 @@
<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.NETWORK_SETTINGS"/>
<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" />
diff --git a/assets/defaultiwlanerrorconfig.json b/assets/defaultiwlanerrorconfig.json
index eae1ae2..7dd5818 100644
--- a/assets/defaultiwlanerrorconfig.json
+++ b/assets/defaultiwlanerrorconfig.json
@@ -75,7 +75,7 @@
},
{
"ErrorType": "IKE_PROTOCOL_ERROR_TYPE",
- "ErrorDetails": ["24"],
+ "ErrorDetails": ["24", "9002"],
"RetryArray": ["10", "20", "40", "80", "160"],
"UnthrottlingEvents": ["APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_CALLING_DISABLE_EVENT"]
}
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index 59fad64..6ab3141 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -116,6 +116,9 @@
private static final int IKE_PROTOCOL_ERROR_PLMN_NOT_ALLOWED = 11011;
private static final int IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055;
+ /** 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;
+
@IntDef({
IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION,
IKE_PROTOCOL_ERROR_MAX_CONNECTION_REACHED,
@@ -132,7 +135,8 @@
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
+ IKE_PROTOCOL_ERROR_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED,
+ IKE_PROTOCOL_ERROR_CONGESTION
})
@interface IkeProtocolErrorType {};
@@ -153,6 +157,10 @@
// String APN as key to identify the ErrorInfo associated with that APN
private 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;
@@ -178,6 +186,11 @@
return mInstances.computeIfAbsent(slotId, k -> new ErrorPolicyManager(context, slotId));
}
+ @VisibleForTesting
+ public static void resetAllInstances() {
+ mInstances.clear();
+ }
+
/**
* Release or reset the instance.
*
@@ -202,6 +215,7 @@
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");
@@ -292,13 +306,19 @@
* @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.IKE_INTERNAL_IO_EXCEPTION) {
ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT;
@@ -362,6 +382,9 @@
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;
break;
@@ -371,6 +394,13 @@
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 +415,23 @@
}
/**
+ * 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
@@ -532,6 +579,7 @@
errorType,
parseErrorDetails(errorType, errorDetailArray),
parseRetryArray((JSONArray) errorTypeObject.get("RetryArray")),
+ errorTypeObject.optInt("NumAttemptsPerFqdn", -1),
parseUnthrottlingEvents(
(JSONArray) errorTypeObject.get("UnthrottlingEvents")));
@@ -778,16 +826,19 @@
@ErrorPolicyErrorType int mErrorType;
List<String> mErrorDetails;
List<Integer> mRetryArray;
+ int mNumAttemptsPerFqdn;
List<Integer> mUnthrottlingEvents;
ErrorPolicy(
@ErrorPolicyErrorType int errorType,
List<String> errorDetails,
List<Integer> retryArray,
+ int numAttemptsPerFqdn,
List<Integer> unthrottlingEvents) {
mErrorType = errorType;
mErrorDetails = errorDetails;
mRetryArray = retryArray;
+ mNumAttemptsPerFqdn = numAttemptsPerFqdn;
mUnthrottlingEvents = unthrottlingEvents;
}
@@ -814,6 +865,16 @@
return retryTime;
}
+ int getCurrentFqdnIndex(int retryIndex, int numFqdns) {
+ int result = -1;
+ if (mNumAttemptsPerFqdn == -1 || mRetryArray.size() <= 0) {
+ return result;
+ }
+ // Cycles between 0 and (numFqdns - 1), based on the current attempt count and size of
+ // mRetryArray.
+ return (retryIndex + 1) / mNumAttemptsPerFqdn % numFqdns;
+ }
+
@ErrorPolicyErrorType
int getErrorType() {
return mErrorType;
@@ -901,6 +962,9 @@
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;
@@ -958,6 +1022,11 @@
return time;
}
+ int getCurrentFqdnIndex(int numFqdns) {
+ ErrorPolicy errorPolicy = getErrorPolicy();
+ return errorPolicy.getCurrentFqdnIndex(mCurrentRetryIndex, numFqdns);
+ }
+
boolean isBackOffTimeValid() {
return mIsBackOffTimeValid;
}
@@ -996,6 +1065,16 @@
}
}
+ static class ApnWithIwlanError {
+ @NonNull final String mApn;
+ @NonNull final IwlanError mIwlanError;
+
+ ApnWithIwlanError(String apn, IwlanError iwlanError) {
+ mApn = apn;
+ mIwlanError = iwlanError;
+ }
+ }
+
private boolean isValidCarrierConfigChangedEvent(int currentCarrierId) {
String errorPolicyConfig =
(String) IwlanHelper.getConfig(KEY_ERROR_POLICY_CONFIG_STRING, mContext, mSlotId);
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index a254535..77207a2 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -31,7 +31,6 @@
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;
@@ -76,12 +75,22 @@
private static final String TAG = IwlanDataService.class.getSimpleName();
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>();
+ @VisibleForTesting Handler mIwlanDataServiceHandler;
+ private HandlerThread mIwlanDataServiceHandlerThread;
+ private static final Map<Integer, IwlanDataServiceProvider> sIwlanDataServiceProviders =
+ new ConcurrentHashMap<>();
+
+ 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;
@VisibleForTesting
enum Transport {
@@ -103,6 +112,7 @@
// 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. */
@@ -138,7 +148,7 @@
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
Log.d(TAG, "onLinkPropertiesChanged: " + linkProperties);
if (isLinkProtocolTypeChanged(linkProperties)) {
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+ for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
dp.dnsPrefetchCheck();
}
}
@@ -180,8 +190,6 @@
private final String SUB_TAG;
private final IwlanDataService mIwlanDataService;
private final IwlanTunnelCallback mIwlanTunnelCallback;
- private HandlerThread mHandlerThread;
- @VisibleForTesting Handler mHandler;
private boolean mWfcEnabled = false;
private boolean mCarrierConfigReady = false;
private EpdgSelector mEpdgSelector;
@@ -189,10 +197,7 @@
private CellInfo mCellInfo = null;
// 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")
+ // Access should be serialized inside IwlanDataServiceHandler
private Map<String, TunnelState> mTunnelStateForApn = new ConcurrentHashMap<>();
// Holds the state of a tunnel (for an APN)
@@ -204,6 +209,7 @@
// 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_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;
@@ -230,7 +236,11 @@
}
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 substract tunnelling overhead
+ }
}
public void setProtocolType(int protocolType) {
@@ -264,7 +274,9 @@
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) {
@@ -345,154 +357,22 @@
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));
- }
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(
+ EVENT_TUNNEL_OPENED,
+ new TunnelOpenedData(
+ apnName, linkProperties, IwlanDataServiceProvider.this)));
}
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);
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(
+ EVENT_TUNNEL_CLOSED,
+ new TunnelClosedData(
+ apnName, error, IwlanDataServiceProvider.this)));
}
}
@@ -637,12 +517,6 @@
}
}
- Looper getLooper() {
- mHandlerThread = new HandlerThread("DSPHandlerThread");
- mHandlerThread.start();
- return mHandlerThread.getLooper();
- }
-
/**
* Constructor
*
@@ -661,18 +535,14 @@
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.CELLINFO_CHANGED_EVENT);
- IwlanEventListener.getInstance(mContext, slotIndex).addEventListener(events, mHandler);
- }
-
- void initHandler() {
- mHandler = new DSPHandler(getLooper());
+ IwlanEventListener.getInstance(mContext, slotIndex)
+ .addEventListener(events, mIwlanDataServiceHandler);
}
@VisibleForTesting
@@ -693,15 +563,22 @@
.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();
+ List<LinkAddress> linkAddrList = tunnelLinkProperties.internalAddresses();
if (linkAddrList.stream().anyMatch(t -> t.isIpv4())) {
try {
gatewayList.add(Inet4Address.getByName("0.0.0.0"));
@@ -717,18 +594,16 @@
}
}
- 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())
@@ -844,128 +719,24 @@
+ ", 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;
- }
+ this);
- 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;
- }
- }
-
- 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 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,
- (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;
- }
- }
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(
+ EVENT_SETUP_DATA_CALL, setupDataCallData));
}
/**
@@ -992,49 +763,26 @@
+ "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);
- }
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.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);
}
}
}
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);
}
}
@@ -1045,11 +793,10 @@
*/
@Override
public void requestDataCallList(DataServiceCallback callback) {
- deliverCallback(
- CALLBACK_TYPE_GET_DATACALL_LIST_COMPLETE,
- DataServiceCallback.RESULT_SUCCESS,
- callback,
- null);
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(
+ EVENT_DATA_CALL_LIST_REQUEST,
+ new DataCallRequestData(callback, IwlanDataServiceProvider.this)));
}
@VisibleForTesting
@@ -1081,23 +828,20 @@
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());
- }
+ 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());
}
}
}
@@ -1125,23 +869,21 @@
IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()));
/* Check if we need to do prefecting */
- synchronized (mTunnelStateForApn) {
- if (networkConnected == true
- && mCarrierConfigReady == true
- && mWfcEnabled == true
- && mTunnelStateForApn.isEmpty()) {
+ 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);
+ // 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);
- }
+ prefetchEpdgServerList(mIwlanDataService.sNetwork, isRoaming);
}
}
@@ -1160,8 +902,8 @@
public void close() {
// TODO: call epdgtunnelmanager.releaseInstance or equivalent
mIwlanDataService.removeDataServiceProvider(this);
- IwlanEventListener.getInstance(mContext, getSlotIndex()).removeEventListener(mHandler);
- mHandlerThread.quit();
+ IwlanEventListener.getInstance(mContext, getSlotIndex())
+ .removeEventListener(mIwlanDataServiceHandler);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -1174,11 +916,9 @@
+ isNetworkConnected(isDDS, 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);
@@ -1187,8 +927,528 @@
}
}
+ 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;
+
+ 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);
+ iwlanDataServiceProvider.mTunnelStats.reportTunnelDown(apnName, tunnelState);
+ iwlanDataServiceProvider.mTunnelStateForApn.remove(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 (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()
+ == IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP) {
+ respBuilder.setCause(
+ ErrorPolicyManager.getInstance(
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex())
+ .getDataFailCause(apnName));
+ respBuilder.setRetryDurationMillis(
+ ErrorPolicyManager.getInstance(
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex())
+ .getCurrentRetryTimeMs(apnName));
+ } else if (tunnelState.getState()
+ == IwlanDataServiceProvider.TunnelState
+ .TUNNEL_IN_FORCE_CLEAN_WAS_IN_BRINGUP) {
+ respBuilder.setCause(DataFailCause.IWLAN_NETWORK_FAILURE);
+ respBuilder.setRetryDurationMillis(5000);
+ }
+
+ 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());
+ 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.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 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;
+ }
+
+ boolean isDDS =
+ IwlanHelper.isDefaultDataSlot(
+ mContext, iwlanDataServiceProvider.getSlotIndex());
+ boolean isCSTEnabled =
+ IwlanHelper.isCrossSimCallingEnabled(
+ mContext, iwlanDataServiceProvider.getSlotIndex());
+ boolean networkConnected = isNetworkConnected(isDDS, isCSTEnabled);
+ Log.d(
+ TAG,
+ "isDds: "
+ + isDDS
+ + ", isCstEnabled: "
+ + isCSTEnabled
+ + ", transport: "
+ + sDefaultDataTransport);
+
+ if (networkConnected == false) {
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ 5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE
+ */,
+ callback,
+ null);
+ return;
+ }
+
+ tunnelState =
+ iwlanDataServiceProvider.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()
+ == IwlanDataServiceProvider.TunnelState.TUNNEL_UP) {
+ Log.w(
+ TAG,
+ "The tunnel for " + dataProfile.getApn() + " already exists.");
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ DataServiceCallback.RESULT_SUCCESS,
+ callback,
+ iwlanDataServiceProvider.apnTunnelStateToDataCallResponse(
+ dataProfile.getApn()));
+ return;
+ } else {
+ Log.e(
+ TAG,
+ "Force close the existing PDN. pduSessionId = "
+ + tunnelState.getPduSessionId()
+ + " Tunnel State = "
+ + tunnelState.getState());
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .closeTunnel(dataProfile.getApn(), true /* forceClose */);
+ iwlanDataServiceProvider.deliverCallback(
+ IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+ 5 /* DataServiceCallback
+ .RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
+ callback,
+ null);
+ return;
+ }
+ }
+
+ 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 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);
+
+ iwlanDataServiceProvider.setTunnelState(
+ dataProfile,
+ callback,
+ IwlanDataServiceProvider.TunnelState.TUNNEL_IN_BRINGUP,
+ null,
+ (reason == DataService.REQUEST_REASON_HANDOVER),
+ pduSessionId);
+
+ boolean result =
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .bringUpTunnel(
+ tunnelReqBuilder.build(),
+ iwlanDataServiceProvider.getIwlanTunnelCallback());
+ Log.d(TAG, "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;
+ int cid = deactivateDataCallData.mCid;
+ reason = deactivateDataCallData.mReason;
+ callback = deactivateDataCallData.mCallback;
+ iwlanDataServiceProvider = deactivateDataCallData.mIwlanDataServiceProvider;
+
+ for (String tunnelApnName :
+ iwlanDataServiceProvider.mTunnelStateForApn.keySet()) {
+ if (tunnelApnName.hashCode() == cid) {
+ /*
+ No need to check state since dataconnection in framework serializes
+ setup and deactivate calls using callId/cid.
+ */
+ iwlanDataServiceProvider
+ .mTunnelStateForApn
+ .get(tunnelApnName)
+ .setState(
+ IwlanDataServiceProvider.TunnelState
+ .TUNNEL_IN_BRINGDOWN);
+ iwlanDataServiceProvider
+ .mTunnelStateForApn
+ .get(tunnelApnName)
+ .setDataServiceCallback(callback);
+ boolean isConnected =
+ isNetworkConnected(
+ IwlanHelper.isDefaultDataSlot(
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex()),
+ IwlanHelper.isCrossSimCallingEnabled(
+ mContext,
+ iwlanDataServiceProvider.getSlotIndex()));
+ iwlanDataServiceProvider
+ .getTunnelManager()
+ .closeTunnel(tunnelApnName, !isConnected);
+ 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;
+
+ IwlanDataServiceProvider dsp =
+ sIwlanDataServiceProviders.remove(
+ iwlanDataServiceProvider.getSlotIndex());
+ if (dsp == null) {
+ Log.w(
+ TAG,
+ "No DataServiceProvider exists for slot "
+ + iwlanDataServiceProvider.getSlotIndex());
+ }
+
+ if (sIwlanDataServiceProviders.isEmpty()) {
+ deinitNetworkCallback();
+ }
+ 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;
+ }
+ }
+
@VisibleForTesting
- static synchronized boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) {
+ static boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) {
if (!isDds && isCstEnabled) {
// Only Non-DDS sub with CST enabled, can use any transport.
return sNetworkConnected;
@@ -1200,9 +1460,6 @@
@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) {
@@ -1210,59 +1467,57 @@
boolean hasTransportChanged = false;
boolean hasNetworkConnectedChanged = false;
- synchronized (IwlanDataService.class) {
- if (sNetworkConnected == networkConnected
- && network.equals(sNetwork)
- && sDefaultDataTransport == transport) {
- // Nothing changed
- return;
- }
+ 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;
- }
+ // safety check
+ if (networkConnected && transport == Transport.UNSPECIFIED_NETWORK) {
+ Log.e(TAG, "setNetworkConnected: Network connected but transport unspecified");
+ return;
+ }
- if (!network.equals(sNetwork)) {
- Log.e(TAG, "setNetworkConnected NW changed from: " + sNetwork + " TO: " + network);
- hasNetworkChanged = true;
- }
+ if (!network.equals(sNetwork)) {
+ Log.e(TAG, "setNetworkConnected NW changed from: " + sNetwork + " TO: " + network);
+ hasNetworkChanged = true;
+ }
- if (transport != sDefaultDataTransport) {
- Log.d(
- TAG,
- "Transport was changed from "
- + sDefaultDataTransport.name()
- + " to "
- + transport.name());
- hasTransportChanged = true;
- }
+ if (transport != sDefaultDataTransport) {
+ Log.d(
+ TAG,
+ "Transport was changed from "
+ + sDefaultDataTransport.name()
+ + " to "
+ + transport.name());
+ hasTransportChanged = true;
+ }
- if (sNetworkConnected != networkConnected) {
- Log.d(
- TAG,
- "Network connected state change from "
- + sNetworkConnected
- + " to "
- + networkConnected);
- hasNetworkConnectedChanged = true;
- }
+ if (sNetworkConnected != networkConnected) {
+ Log.d(
+ TAG,
+ "Network connected state change from "
+ + sNetworkConnected
+ + " to "
+ + networkConnected);
+ hasNetworkConnectedChanged = true;
+ }
- sNetworkConnected = networkConnected;
- sDefaultDataTransport = transport;
- sNetwork = network;
- if (!networkConnected) {
- // reset link protocol type
- sLinkProtocolType = LinkProtocolType.UNKNOWN;
- }
+ sNetworkConnected = networkConnected;
+ sDefaultDataTransport = transport;
+ sNetwork = network;
+ if (!networkConnected) {
+ // reset link protocol type
+ sLinkProtocolType = LinkProtocolType.UNKNOWN;
}
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 +1527,14 @@
}
// only prefetch dns and updateNetwork if Network has changed
if (hasNetworkChanged) {
- for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
+ for (IwlanDataServiceProvider dp : sIwlanDataServiceProviders.values()) {
dp.dnsPrefetchCheck();
dp.updateNetwork(sNetwork);
}
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
@@ -1338,16 +1593,7 @@
* @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() {
@@ -1359,43 +1605,53 @@
// TODO: validity check on slot index
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);
+ if (mIwlanDataServiceHandler == null) {
+ initHandler();
+ }
- // register for default network callback
+ if (mNetworkMonitorCallback == null) {
+ // start monitoring network and register for default network callback
ConnectivityManager connectivityManager =
mContext.getSystemService(ConnectivityManager.class);
mNetworkMonitorCallback = new IwlanNetworkMonitorCallback();
- connectivityManager.registerDefaultNetworkCallback(mNetworkMonitorCallback, handler);
+ connectivityManager.registerSystemDefaultNetworkCallback(
+ mNetworkMonitorCallback, mIwlanDataServiceHandler);
Log.d(TAG, "Registered with Connectivity Service");
}
IwlanDataServiceProvider dp = new IwlanDataServiceProvider(slotIndex, this);
- addIwlanDataServiceProvider(dp);
+
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.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;
- }
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.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);
+ connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback);
+ mNetworkMonitorCallback = null;
+ if (mIwlanDataServiceHandlerThread != null) {
+ mIwlanDataServiceHandlerThread.quit();
+ mIwlanDataServiceHandlerThread = null;
+ }
+ mIwlanDataServiceHandler = null;
}
@VisibleForTesting
@@ -1408,6 +1664,51 @@
return mNetworkMonitorCallback;
}
+ @VisibleForTesting
+ void initHandler() {
+ mIwlanDataServiceHandler = new IwlanDataServiceHandler(getLooper());
+ }
+
+ @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.CELLINFO_CHANGED_EVENT:
+ return "CELLINFO_CHANGED_EVENT";
+ default:
+ return "Unknown(" + event + ")";
+ }
+ }
+
@Override
public void onCreate() {
setAppContext(getApplicationContext());
@@ -1429,11 +1730,8 @@
@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();
- }
+ mIwlanDataServiceHandler.sendMessage(
+ mIwlanDataServiceHandler.obtainMessage(EVENT_FORCE_CLOSE_TUNNEL));
return super.onUnbind(intent);
}
@@ -1446,7 +1744,7 @@
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/IwlanEventListener.java b/src/com/google/android/iwlan/IwlanEventListener.java
index b4d38e2..5cb98e9 100644
--- a/src/com/google/android/iwlan/IwlanEventListener.java
+++ b/src/com/google/android/iwlan/IwlanEventListener.java
@@ -85,6 +85,16 @@
/** On Cellinfo changed */
public static final int CELLINFO_CHANGED_EVENT = 11;
+ /* Events used and handled by IwlanDataService internally */
+ public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100;
+
+ public static final int DATA_SERVICE_INTERNAL_EVENT_END = 200;
+
+ /* Events used and handled by IwlanNetworkService internally */
+ public static final int NETWORK_SERVICE_INTERNAL_EVENT_BASE = 200;
+
+ public static final int NETWORK_SERVICE_INTERNAL_EVENT_END = 300;
+
@IntDef({
CARRIER_CONFIG_CHANGED_EVENT,
WIFI_DISABLE_EVENT,
@@ -152,11 +162,16 @@
}
}
- /** Returns IwlanEventListener instance */
+ /** c 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.
*
@@ -485,7 +500,7 @@
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();
}
}
}
@@ -494,7 +509,7 @@
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, 0 /* unused */, arrayCi).sendToTarget();
}
}
}
diff --git a/src/com/google/android/iwlan/IwlanNetworkService.java b/src/com/google/android/iwlan/IwlanNetworkService.java
index 6adbaff..361eaa1 100644
--- a/src/com/google/android/iwlan/IwlanNetworkService.java
+++ b/src/com/google/android/iwlan/IwlanNetworkService.java
@@ -26,6 +26,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -43,16 +44,24 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
public class IwlanNetworkService extends NetworkService {
private static final String TAG = IwlanNetworkService.class.getSimpleName();
private 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 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 {
@@ -63,6 +72,7 @@
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
@@ -133,7 +143,7 @@
*/
@Override
public void onSubscriptionsChanged() {
- for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) {
+ for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviders.values()) {
np.subscriptionChanged();
}
}
@@ -144,45 +154,6 @@
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 +166,19 @@
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, mIwlanNetworkServiceHandler);
}
@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());
+ mIwlanNetworkServiceHandler.sendMessage(
+ mIwlanNetworkServiceHandler.obtainMessage(
+ EVENT_NETWORK_REGISTRATION_INFO_REQUEST,
+ new NetworkRegistrationInfoRequestData(domain, callback, this)));
}
/**
@@ -252,8 +189,8 @@
@Override
public void close() {
mIwlanNetworkService.removeNetworkServiceProvider(this);
- IwlanEventListener.getInstance(mContext, getSlotIndex()).removeEventListener(mHandler);
- mHandlerThread.quit();
+ IwlanEventListener.getInstance(mContext, getSlotIndex())
+ .removeEventListener(mIwlanNetworkServiceHandler);
}
@VisibleForTesting
@@ -277,6 +214,126 @@
}
}
+ 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;
+
+ switch (msg.what) {
+ case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT:
+ iwlanNetworkServiceProvider = getNetworkServiceProvider(msg.arg1);
+ iwlanNetworkServiceProvider.notifyNetworkRegistrationInfoChanged();
+ break;
+
+ 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(
+ Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setEmergencyOnly(!iwlanNetworkServiceProvider.mIsSubActive)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS);
+
+ if (!IwlanNetworkService.isNetworkConnected(
+ IwlanHelper.isDefaultDataSlot(
+ mContext, iwlanNetworkServiceProvider.getSlotIndex()),
+ IwlanHelper.isCrossSimCallingEnabled(
+ mContext, iwlanNetworkServiceProvider.getSlotIndex()))) {
+ nriBuilder
+ .setRegistrationState(
+ NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_SEARCHING)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ Log.d(TAG, "reg state REGISTRATION_STATE_NOT_REGISTERED_SEARCHING");
+ } else {
+ nriBuilder
+ .setRegistrationState(
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN);
+ Log.d(TAG, "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;
+ IwlanNetworkServiceProvider nsp =
+ sIwlanNetworkServiceProviders.remove(
+ iwlanNetworkServiceProvider.getSlotIndex());
+ if (nsp == null) {
+ Log.w(
+ TAG,
+ "No NetworkServiceProvider exists for slot "
+ + iwlanNetworkServiceProvider.getSlotIndex());
+ 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,31 +349,14 @@
// 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");
+ if (mIwlanNetworkServiceHandler == null) {
+ initHandler();
}
IwlanNetworkServiceProvider np = new IwlanNetworkServiceProvider(slotIndex, this);
- sIwlanNetworkServiceProviderList.add(np);
+ mIwlanNetworkServiceHandler.sendMessage(
+ mIwlanNetworkServiceHandler.obtainMessage(
+ EVENT_CREATE_NETWORK_SERVICE_PROVIDER, np));
return np;
}
@@ -340,28 +380,61 @@
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) {
+ mIwlanNetworkServiceHandler.sendMessage(
+ mIwlanNetworkServiceHandler.obtainMessage(
+ EVENT_REMOVE_NETWORK_SERVICE_PROVIDER, np));
+ }
+
+ void initCallback() {
+ // register for default network callback
+ ConnectivityManager connectivityManager =
+ mContext.getSystemService(ConnectivityManager.class);
+ mNetworkMonitorCallback = new IwlanNetworkMonitorCallback();
+ connectivityManager.registerSystemDefaultNetworkCallback(
+ mNetworkMonitorCallback, mIwlanNetworkServiceHandler);
+ Log.d(TAG, "Registered with Connectivity Service");
+
+ /* register with subscription manager */
+ SubscriptionManager subscriptionManager =
+ mContext.getSystemService(SubscriptionManager.class);
+ mSubsChangeListener = new IwlanOnSubscriptionsChangedListener();
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ new HandlerExecutor(mIwlanNetworkServiceHandler), mSubsChangeListener);
+ Log.d(TAG, "Registered with Subscription Service");
+ }
+
+ void deinitCallback() {
+ // deinit network related stuff
+ ConnectivityManager connectivityManager =
+ mContext.getSystemService(ConnectivityManager.class);
+ connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback);
+ mNetworkMonitorCallback = null;
+
+ // deinit subscription manager related stuff
+ SubscriptionManager subscriptionManager =
+ mContext.getSystemService(SubscriptionManager.class);
+ subscriptionManager.removeOnSubscriptionsChangedListener(mSubsChangeListener);
+ mSubsChangeListener = null;
+ if (mIwlanNetworkServiceHandlerThread != null) {
+ mIwlanNetworkServiceHandlerThread.quit();
+ mIwlanNetworkServiceHandlerThread = null;
+ }
+ mIwlanNetworkServiceHandler = null;
}
Context getContext() {
@@ -375,12 +448,36 @@
@VisibleForTesting
IwlanNetworkServiceProvider getNetworkServiceProvider(int slotIndex) {
- for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) {
- if (np.getSlotIndex() == slotIndex) {
- return np;
- }
+ return sIwlanNetworkServiceProviders.get(slotIndex);
+ }
+
+ @VisibleForTesting
+ void initHandler() {
+ mIwlanNetworkServiceHandler = new IwlanNetworkServiceHandler(getLooper());
+ }
+
+ @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
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index 54b082f..d4f168a 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -19,6 +19,7 @@
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.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 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;
@@ -54,6 +57,8 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
public class EpdgSelector {
private static final String TAG = "EpdgSelector";
@@ -65,6 +70,9 @@
private int mV6PcoId = -1;
private byte[] mV4PcoData = null;
private byte[] mV6PcoData = null;
+ @NonNull private ErrorPolicyManager mErrorPolicyManager;
+
+ private static final long DNS_RESOLVER_TIMEOUT_DURATION_SEC = 5L;
final Comparator<InetAddress> inetAddressComparator =
new Comparator<InetAddress>() {
@@ -98,6 +106,8 @@
EpdgSelector(Context context, int slotId) {
mContext = context;
mSlotId = slotId;
+
+ mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId);
}
public static EpdgSelector getSelectorInstance(Context context, int slotId) {
@@ -143,57 +153,81 @@
mV6PcoData = null;
}
- private void getIP(
- String domainName, int filter, ArrayList<InetAddress> validIpList, Network network) {
- InetAddress[] ipList;
+ private Map.Entry<String, List<InetAddress>> getIP(
+ 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);
- }
- 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);
- }
+ if (InetAddresses.isNumericAddress(domainName)) {
+ try {
+ Log.d(TAG, domainName + " is a numeric ip address");
+ ipList.add(InetAddress.getByName(domainName));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e);
}
- } catch (Exception e) {
- Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e);
+ } 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);
+ }
+
+ @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);
+ 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) {
+ if (Thread.currentThread().interrupted()) {
+ Thread.currentThread().interrupt();
+ }
+ Log.e(TAG, "InterruptedException: ", e);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "TimeoutException: ", e);
+ }
}
+
+ List<InetAddress> filteredIpList = new ArrayList<>();
+ // Filter the IP list by input ProtoFilter
+ for (InetAddress ipAddress : ipList) {
+ switch (filter) {
+ case PROTO_FILTER_IPV4:
+ if (ipAddress instanceof Inet4Address) {
+ filteredIpList.add(ipAddress);
+ }
+ break;
+ case PROTO_FILTER_IPV6:
+ if (!IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) {
+ filteredIpList.add(ipAddress);
+ }
+ break;
+ case PROTO_FILTER_IPV4V6:
+ filteredIpList.add(ipAddress);
+ break;
+ default:
+ Log.d(TAG, "Invalid ProtoFilter : " + filter);
+ }
+ }
+ validIpList.addAll(filteredIpList);
+
+ return Map.entry(domainName, filteredIpList);
}
private String[] getPlmnList() {
@@ -270,7 +304,7 @@
return combinedList.toArray(new String[combinedList.size()]);
}
- private ArrayList<InetAddress> removeDuplicateIp(ArrayList<InetAddress> validIpList) {
+ private ArrayList<InetAddress> removeDuplicateIp(List<InetAddress> validIpList) {
ArrayList<InetAddress> resultIpList = new ArrayList<InetAddress>();
for (Iterator<InetAddress> iterator = validIpList.iterator(); iterator.hasNext(); ) {
@@ -304,7 +338,7 @@
}
private void resolutionMethodStatic(
- int filter, ArrayList<InetAddress> validIpList, boolean isRoaming, Network network) {
+ int filter, List<InetAddress> validIpList, boolean isRoaming, Network network) {
String[] domainNames = null;
Log.d(TAG, "STATIC Method");
@@ -359,10 +393,11 @@
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();
+ Map<String, List<InetAddress>> domainNameToIpAddr = new LinkedHashMap<>();
Log.d(TAG, "PLMN Method");
@@ -377,22 +412,35 @@
* 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");
+ getIP(domainName.toString(), filter, validIpList, network);
+ 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);
+ Map.Entry<String, List<InetAddress>> entry =
+ getIP(domainName.toString(), filter, validIpList, network);
+ domainNameToIpAddr.put(entry.getKey(), entry.getValue());
domainName.setLength(0);
}
+ 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();
@@ -514,7 +562,7 @@
private void lacDomainNameResolution(
int filter,
- ArrayList<InetAddress> validIpList,
+ List<InetAddress> validIpList,
String lacString,
boolean isEmergency,
Network network) {
@@ -548,7 +596,7 @@
}
}
- 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 +634,7 @@
}
}
- 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 +695,7 @@
private void processNaptrResponse(
int filter,
- ArrayList<InetAddress> validIpList,
+ List<InetAddress> validIpList,
boolean isEmergency,
Network network,
boolean isRegisteredWith3GPP,
@@ -699,7 +747,7 @@
}
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);
@@ -777,7 +825,8 @@
NaptrDnsResolver.query(network, domainName.toString(), r -> r.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(
@@ -797,6 +846,8 @@
Thread.currentThread().interrupt();
}
Log.e(TAG, "InterruptedException: ", e);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "TimeoutException: ", e);
}
}
@@ -807,13 +858,11 @@
boolean isEmergency,
@NonNull Network network,
EpdgSelectorCallback selectorCallback) {
- ArrayList<InetAddress> validIpList = new ArrayList<InetAddress>();
- StringBuilder domainName = new StringBuilder();
Runnable doValidation =
() -> {
+ List<InetAddress> validIpList = new ArrayList<>();
Log.d(TAG, "Processing request with transactionId: " + transactionId);
- String[] plmnList;
int[] addrResolutionMethods =
IwlanHelper.getConfig(
@@ -834,6 +883,7 @@
resolutionMethodVisitedCountry(filter, validIpList, isEmergency, network);
}
+ Map<String, List<InetAddress>> plmnDomainNamesToIpAddress = null;
for (int addrResolutionMethod : addrResolutionMethods) {
switch (addrResolutionMethod) {
case CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC:
@@ -841,7 +891,9 @@
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,6 +914,23 @@
}
if (selectorCallback != null) {
+ if (mErrorPolicyManager.getMostRecentDataFailCause()
+ == DataFailCause.IWLAN_CONGESTION) {
+ int numFqdns = plmnDomainNamesToIpAddress.size();
+ int index = mErrorPolicyManager.getCurrentFqdnIndex(numFqdns);
+ if (index >= 0 && index < numFqdns) {
+ Object[] keys = plmnDomainNamesToIpAddress.keySet().toArray();
+ validIpList = plmnDomainNamesToIpAddress.get(keys[index]);
+ } else {
+ Log.w(
+ TAG,
+ "CONGESTION error handling- invalid index: "
+ + index
+ + " number of PLMN FQDNs: "
+ + numFqdns);
+ }
+ }
+
if (!validIpList.isEmpty()) {
Collections.sort(validIpList, inetAddressComparator);
selectorCallback.onServerListChanged(
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index ea78893..5c87b7a 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -625,7 +625,7 @@
}
/**
- * Gets a epdg tunnel manager instance.
+ * Gets a EpdgTunnelManager instance.
*
* @param context application context
* @param subId subscription ID for the tunnel
@@ -636,6 +636,11 @@
subId, k -> new EpdgTunnelManager(context, subId));
}
+ @VisibleForTesting
+ public static void resetAllInstances() {
+ mTunnelManagerInstances.clear();
+ }
+
public interface TunnelCallback {
/**
* Called when the tunnel is opened.
@@ -989,13 +994,17 @@
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) {
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index 3bc624b..aee16f1 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -85,6 +85,7 @@
doReturn(mockAssetManager).when(mMockContext).getAssets();
doReturn(is).when(mockAssetManager).open(any());
setupMockForCarrierConfig(null);
+ ErrorPolicyManager.resetAllInstances();
mErrorPolicyManager = spy(ErrorPolicyManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX));
}
@@ -232,6 +233,20 @@
assertEquals(160, time);
time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
assertEquals(86400, time);
+
+ iwlanError = buildIwlanIkeProtocolError(9002);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ 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);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(160, time);
+ time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+ assertEquals(86400, time);
}
@Test
@@ -661,6 +676,85 @@
}
@Test
+ public void testErrorPolicyWithNumAttemptsPerFqdn() throws Exception {
+ String apn = "ims";
+ String config =
+ "[{"
+ + "\"ApnName\": \""
+ + apn
+ + "\","
+ + "\"ErrorTypes\": [{"
+ + getErrorTypeInJSON(
+ "IKE_PROTOCOL_ERROR_TYPE", /* ErrorType */
+ new String[] {"15500"}, /* ErrorDetails ("CONGESTION") */
+ "6", /* NumAttemptsPerFqdn */
+ new String[] {
+ "0", "0", "300", "600", "1200", "0", "0", "0", "300", "600",
+ "1200", "-1"
+ }, /* RetryArray */
+ new String[] {
+ "APM_ENABLE_EVENT",
+ "WIFI_DISABLE_EVENT",
+ "WIFI_CALLING_DISABLE_EVENT"
+ }) /* UnthrottlingEvents */
+ + "}]"
+ + "}]";
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ErrorPolicyManager.KEY_ERROR_POLICY_CONFIG_STRING, config);
+ setupMockForCarrierConfig(bundle);
+ mErrorPolicyManager
+ .mHandler
+ .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ .sendToTarget();
+
+ sleep(1000);
+ 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));
+
+ 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 testErrorStats() throws Exception {
String apn1 = "ims";
String apn2 = "mms";
@@ -720,6 +814,29 @@
+ "\"]";
}
+ private String getErrorTypeInJSON(
+ String ErrorType,
+ String[] errorDetails,
+ String numAttemptsPerFqdn,
+ String[] retryArray,
+ String[] unthrottlingEvents) {
+ return "\"ErrorType\": \""
+ + ErrorType
+ + "\","
+ + "\"ErrorDetails\": [\""
+ + String.join("\", \"", errorDetails)
+ + "\"],"
+ + "\"NumAttemptsPerFqdn\": \""
+ + numAttemptsPerFqdn
+ + "\","
+ + "\"RetryArray\": [\""
+ + String.join("\", \"", retryArray)
+ + "\"],"
+ + "\"UnthrottlingEvents\": [\""
+ + String.join("\", \"", unthrottlingEvents)
+ + "\"]";
+ }
+
private void sleep(long time) {
try {
Thread.sleep(time);
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index a0414de..607b751 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -16,7 +16,6 @@
package com.google.android.iwlan;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -32,6 +31,7 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.os.test.TestLooper;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.DataFailCause;
import android.telephony.SubscriptionInfo;
@@ -96,7 +96,6 @@
@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;
@@ -116,6 +115,7 @@
private IwlanDataService mIwlanDataService;
private IwlanDataServiceProvider mIwlanDataServiceProvider;
private IwlanDataServiceProvider mSpyIwlanDataServiceProvider;
+ private TestLooper mTestLooper = new TestLooper();
private final class IwlanDataServiceCallback extends IDataServiceCallback.Stub {
@@ -178,10 +178,6 @@
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);
@@ -214,10 +210,12 @@
when(mMockIPv6LinkAddress.getAddress()).thenReturn(mMockInet6Address);
mIwlanDataService = spy(new IwlanDataService());
+ doReturn(mTestLooper.getLooper()).when(mIwlanDataService).getLooper();
mIwlanDataService.setAppContext(mMockContext);
mIwlanDataServiceProvider =
(IwlanDataServiceProvider)
mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX);
+ mTestLooper.dispatchAll();
mSpyIwlanDataServiceProvider = spy(mIwlanDataServiceProvider);
}
@@ -225,34 +223,64 @@
public void cleanUp() throws Exception {
mStaticMockSession.finishMocking();
mIwlanDataServiceProvider.close();
+ mTestLooper.dispatchAll();
if (mIwlanDataService != null) {
mIwlanDataService.onDestroy();
}
}
- @Test
- public void testWifiOnAvailable() {
- IwlanNetworkMonitorCallback mNetworkMonitorCallback =
+ private void verifiyNetworkConnected(int transportType) {
+ NetworkCapabilities mockNetworkCapabilities = mock(NetworkCapabilities.class);
+
+ when(mockNetworkCapabilities.hasTransport(anyInt())).thenReturn(false);
+ when(mockNetworkCapabilities.hasTransport(eq(transportType))).thenReturn(true);
+
+ IwlanNetworkMonitorCallback networkMonitorCallback =
mIwlanDataService.getNetworkMonitorCallback();
+ networkMonitorCallback.onCapabilitiesChanged(mMockNetwork, mockNetworkCapabilities);
+ }
- mNetworkMonitorCallback.onAvailable(mMockNetwork);
- boolean ret = mIwlanDataService.isNetworkConnected(true, false);
+ private void verifiyNetworkLost(int transportType) {
+ IwlanNetworkMonitorCallback networkMonitorCallback =
+ mIwlanDataService.getNetworkMonitorCallback();
+ networkMonitorCallback.onLost(mMockNetwork);
+ }
- assertTrue(ret);
+ @Test
+ public void testWifionConnected() {
+ verifiyNetworkConnected(TRANSPORT_WIFI);
+ assertTrue(mIwlanDataService.isNetworkConnected(false, false));
}
@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);
+ verifiyNetworkLost(TRANSPORT_WIFI);
+ assertFalse(mIwlanDataService.isNetworkConnected(false, false));
verify(mMockIwlanDataServiceProvider).forceCloseTunnelsInDeactivatingState();
mIwlanDataService.removeDataServiceProvider(mMockIwlanDataServiceProvider);
+ mTestLooper.dispatchAll();
+ }
+
+ @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();
+ assertEquals(mIwlanDataService.mIwlanDataServiceHandler, null);
+ mIwlanDataService.onCreateDataServiceProvider(DEFAULT_SLOT_INDEX);
+ mTestLooper.dispatchAll();
}
@Test
@@ -275,6 +303,7 @@
false,
1);
mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
+ mTestLooper.dispatchAll();
latch.await(1, TimeUnit.SECONDS);
assertEquals(mResultCode, DataServiceCallback.RESULT_SUCCESS);
@@ -319,6 +348,7 @@
latch = new CountDownLatch(1);
IwlanDataServiceCallback callback = new IwlanDataServiceCallback("requestDataCallList");
mIwlanDataServiceProvider.requestDataCallList(new DataServiceCallback(callback));
+ mTestLooper.dispatchAll();
latch.await(1, TimeUnit.SECONDS);
assertEquals(mResultCode, DataServiceCallback.RESULT_SUCCESS);
@@ -339,6 +369,7 @@
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, timeout(1000).times(1))
.onSetupDataCallComplete(
@@ -365,10 +396,12 @@
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
@@ -377,6 +410,7 @@
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));
@@ -403,6 +437,7 @@
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
@@ -412,6 +447,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -438,6 +474,7 @@
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
@@ -450,6 +487,7 @@
ArgumentCaptor.forClass(DataCallResponse.class);
mSpyIwlanDataServiceProvider.getIwlanTunnelCallback().onOpened(TEST_APN_NAME, tp);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
@@ -473,6 +511,7 @@
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
DataService.REQUEST_REASON_NORMAL /* DataService.REQUEST_REASON_NORMAL */,
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check closeTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1)).closeTunnel(eq(TEST_APN_NAME), anyBoolean());
@@ -481,6 +520,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS));
}
@@ -507,6 +547,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
ArgumentCaptor.forClass(DataCallResponse.class);
@@ -545,6 +586,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
ArgumentCaptor.forClass(DataCallResponse.class);
@@ -574,17 +616,22 @@
when(mMockLinkProperties.getLinkAddresses()).thenReturn(linkAddresses);
mNetworkMonitorCallback.onLinkPropertiesChanged(mMockNetwork, mMockLinkProperties);
- mIwlanDataServiceProvider
- .mHandler
- .obtainMessage(IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT)
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.CARRIER_CONFIG_CHANGED_EVENT,
+ DEFAULT_SLOT_INDEX,
+ 0 /* unused */)
.sendToTarget();
- sleep(1000);
- mIwlanDataServiceProvider
- .mHandler
- .obtainMessage(IwlanEventListener.WIFI_CALLING_ENABLE_EVENT)
+ mIwlanDataService
+ .mIwlanDataServiceHandler
+ .obtainMessage(
+ IwlanEventListener.WIFI_CALLING_ENABLE_EVENT,
+ DEFAULT_SLOT_INDEX,
+ 0 /* unused */)
.sendToTarget();
- sleep(1000);
+ mTestLooper.dispatchAll();
linkAddresses.add(mMockIPv6LinkAddress);
@@ -673,10 +720,12 @@
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
@@ -714,6 +763,7 @@
null, /* trafficDescriptor */
true, /* matchAllRuleAllowed */
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
/* Check bringUpTunnel() is called. */
verify(mMockEpdgTunnelManager, times(1))
@@ -723,6 +773,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, times(1))
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -836,6 +887,7 @@
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));
@@ -857,12 +909,14 @@
doReturn(true)
.when(mMockEpdgTunnelManager)
.bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+ mTestLooper.dispatchAll();
sleep(sleepTime);
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onOpened(TEST_APN_NAME, mMockTunnelLinkProperties);
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, atLeastOnce())
.onSetupDataCallComplete(
eq(DataServiceCallback.RESULT_SUCCESS), any(DataCallResponse.class));
@@ -872,6 +926,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.IKE_INTERNAL_IO_EXCEPTION));
+ mTestLooper.dispatchAll();
}
private void mockDeactivateTunnelDown(long sleepTime) {
@@ -879,6 +934,7 @@
TEST_APN_NAME.hashCode() /* cid: hashcode() of "ims" */,
DataService.REQUEST_REASON_NORMAL /* DataService.REQUEST_REASON_NORMAL */,
mMockDataServiceCallback);
+ mTestLooper.dispatchAll();
verify(mMockEpdgTunnelManager, atLeastOnce()).closeTunnel(eq(TEST_APN_NAME), anyBoolean());
sleep(sleepTime);
@@ -886,6 +942,7 @@
mSpyIwlanDataServiceProvider
.getIwlanTunnelCallback()
.onClosed(TEST_APN_NAME, new IwlanError(IwlanError.NO_ERROR));
+ mTestLooper.dispatchAll();
verify(mMockDataServiceCallback, atLeastOnce())
.onDeactivateDataCallComplete(eq(DataServiceCallback.RESULT_SUCCESS));
}
diff --git a/test/com/google/android/iwlan/IwlanEventListenerTest.java b/test/com/google/android/iwlan/IwlanEventListenerTest.java
index 789ab25..4e127ca 100644
--- a/test/com/google/android/iwlan/IwlanEventListenerTest.java
+++ b/test/com/google/android/iwlan/IwlanEventListenerTest.java
@@ -93,15 +93,14 @@
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 +110,7 @@
when(mMockTelephonyManager.createForSubscriptionId(eq(0)))
.thenReturn(mMockTelephonyManager);
+ IwlanEventListener.resetAllInstances();
mIwlanEventListener = IwlanEventListener.getInstance(mMockContext, DEFAULT_SLOT_INDEX);
}
@@ -121,7 +121,10 @@
@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 +143,10 @@
@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 +167,10 @@
@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 +191,15 @@
@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 +231,15 @@
@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 +262,11 @@
.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>();
diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
index 9a4af93..804e892 100644
--- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
@@ -49,6 +49,7 @@
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 +76,9 @@
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_IPV6_ADDRESS = "0000:0000:0000:0000:0000:0000:0000:0001";
private static int testPcoIdIPv6 = 0xFF01;
@@ -86,6 +90,7 @@
@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,8 +113,14 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mStaticMockSession = mockitoSession().mockStatic(DnsResolver.class).startMocking();
+ mStaticMockSession =
+ mockitoSession()
+ .mockStatic(DnsResolver.class)
+ .mockStatic(ErrorPolicyManager.class)
+ .startMocking();
+ when(ErrorPolicyManager.getInstance(mMockContext, DEFAULT_SLOT_INDEX))
+ .thenReturn(mMockErrorPolicyManager);
mEpdgSelector = new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX);
when(mMockContext.getSystemService(eq(SubscriptionManager.class)))
@@ -229,18 +240,9 @@
}
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";
+ 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,
@@ -249,17 +251,33 @@
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);
+ mFakeDns.setAnswer(expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+ mFakeDns.setAnswer(expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
+ mFakeDns.setAnswer(expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
+ mFakeDns.setAnswer(
+ "sos." + expectedFqdnFromHplmn, new String[] {TEST_IP_ADDRESS_3}, TYPE_A);
+ mFakeDns.setAnswer(
+ "sos." + expectedFqdnFromEHplmn, new String[] {TEST_IP_ADDRESS_4}, TYPE_A);
+ mFakeDns.setAnswer(
+ "sos." + expectedFqdnFromConfig, new String[] {TEST_IP_ADDRESS_5}, 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_3), testInetAddresses.get(0));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(1));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_4), testInetAddresses.get(2));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(3));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_5), testInetAddresses.get(4));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(5));
+ } else {
+ assertEquals(3, testInetAddresses.size());
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS), testInetAddresses.get(0));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_1), testInetAddresses.get(1));
+ assertEquals(InetAddress.getByName(TEST_IP_ADDRESS_2), testInetAddresses.get(2));
+ }
}
@Test
@@ -298,7 +316,7 @@
mEpdgSelector.getValidatedServerList(
1234,
EpdgSelector.PROTO_FILTER_IPV4V6,
- false /*isRoaming*/,
+ false /* isRoaming */,
isEmergency,
mMockNetwork,
new EpdgSelector.EpdgSelectorCallback() {
@@ -492,7 +510,7 @@
// 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;
}
}
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index f2cba73..6277f3f 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -63,6 +63,7 @@
import com.google.android.iwlan.IwlanError;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -150,10 +151,20 @@
@Mock IpSecTransform mMockedIpSecTransformOut;
@Mock LinkProperties mMockLinkProperties;
- ArgumentCaptor<ChildSessionCallback> mChildSessionCallbackCaptor;
+ 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);
@@ -162,7 +173,7 @@
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(
@@ -192,8 +203,6 @@
when(mMockIkeSessionConnectionInfo.getNetwork()).thenReturn(mMockNetwork);
- mChildSessionCallbackCaptor = ArgumentCaptor.forClass(ChildSessionCallback.class);
-
doReturn(EXPECTED_LOCAL_ADDRESSES)
.when(mEpdgTunnelManager)
.getAddressForNetwork(any(), any());
@@ -339,6 +348,7 @@
}
@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 +380,7 @@
}
@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 +410,7 @@
}
@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);
@@ -465,6 +477,32 @@
}
@Test
+ public void testInitialContactForFirstTunnelOnly() throws Exception {
+ final String firstApnName = "ims";
+ final String secondApnName = "mms";
+
+ IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(firstApnName, null);
+ 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";
@@ -1094,9 +1132,10 @@
mEpdgTunnelManager.setIsEpdgAddressSelected(true);
}
- private ChildSessionCallback verifyBringUpTunnelWithDnsQuery(
+ private IkeSessionArgumentCaptors verifyBringUpTunnelWithDnsQuery(
String apnName, IkeSession ikeSession) {
reset(mMockIwlanTunnelCallback);
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors = new IkeSessionArgumentCaptors();
verifyBringUpTunnel(apnName, true /* needPendingBringUpReq */);
@@ -1104,11 +1143,11 @@
.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,28 +1156,29 @@
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));
@@ -1152,17 +1192,13 @@
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();
- }
-
- private void verifyTunnelOnOpened(String apnName) {
- verifyTunnelOnOpened(apnName, mChildSessionCallbackCaptor.getValue());
+ return ikeSessionArgumentCaptors;
}
private void verifyTunnelOnOpened(String apnName, ChildSessionCallback childSessionCallback) {
@@ -1187,8 +1223,11 @@
setOneTunnelOpened(openedApnName);
- verifyBringUpTunnel(toBeOpenedApnName, false /* needPendingBringUpReq */);
- verifyTunnelOnOpened(toBeOpenedApnName);
+ IkeSessionArgumentCaptors ikeSessionArgumentCaptors =
+ verifyBringUpTunnel(toBeOpenedApnName, false /* needPendingBringUpReq */);
+ ChildSessionCallback childSessionCallback =
+ ikeSessionArgumentCaptors.mChildSessionCallbackCaptor.getValue();
+ verifyTunnelOnOpened(toBeOpenedApnName, childSessionCallback);
}
@Test
@@ -1196,10 +1235,18 @@
final String firstApnName = "ims";
final String secondApnName = "mms";
- ChildSessionCallback firstCallback = verifyBringUpTunnelWithDnsQuery(firstApnName, null);
- verifyBringUpTunnel(secondApnName, true /* needPendingBringUpReq */);
+ IkeSessionArgumentCaptors firstTunnelArgumentCaptors =
+ verifyBringUpTunnelWithDnsQuery(firstApnName, null);
+ 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
@@ -1286,8 +1333,10 @@
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,8 +1349,10 @@
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);
@@ -1319,8 +1370,10 @@
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);