blob: ca8abeb603d5ed0d77273080cede86c5b6cdaa50 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony.dataconnection;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.net.InetAddresses;
import android.net.KeepalivePacketData;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketData;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RegState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
import android.telephony.data.TrafficDescriptor;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import com.android.internal.R;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
import com.android.internal.telephony.metrics.DataCallSessionStats;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Consumer;
public class DataConnectionTest extends TelephonyTest {
private static final int DEFAULT_DC_CID = 10;
@Mock
DcTesterFailBringUpAll mDcTesterFailBringUpAll;
@Mock
ConnectionParams mCp;
@Mock
DisconnectParams mDcp;
@Mock
ApnContext mApnContext;
@Mock
ApnContext mEnterpriseApnContext;
@Mock
DcFailBringUp mDcFailBringUp;
@Mock
DataCallSessionStats mDataCallSessionStats;
@Mock
DataConnection mDefaultDc;
@Mock
DataServiceManager mDataServiceManager;
private DataConnection mDc;
private DataConnectionTestHandler mDataConnectionTestHandler;
private DcController mDcc;
private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
ApnSetting.PROTOCOL_IP, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
""); // mnvo_match_data
private ApnSetting mApn2 = ApnSetting.makeApnSetting(
2164, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, // types
ApnSetting.PROTOCOL_IP, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
""); // mnvo_match_data
private ApnSetting mApn3 = ApnSetting.makeApnSetting(
2165, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_DEFAULT, // types
ApnSetting.PROTOCOL_IPV6, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
"", // mnvo_match_data
0, // apn_set_id
-1, // carrier_id
1); // skip_464xlat
private ApnSetting mApn4 = ApnSetting.makeApnSetting(
2166, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_IMS, // types
ApnSetting.PROTOCOL_IPV6, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
""); // mnvo_match_data
private ApnSetting mApn5 = ApnSetting.makeApnSetting(
2167, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_IMS, // types
ApnSetting.PROTOCOL_IPV6, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
"", // mnvo_match_data
0, // apn_set_id
-1, // carrier_id
0); // skip_464xlat
private ApnSetting mApn6 = ApnSetting.makeApnSetting(
2168, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
null, // proxy
-1, // port
null, // mmsc
null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
ApnSetting.TYPE_EMERGENCY, // types
ApnSetting.PROTOCOL_IP, // protocol
ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
-1, // mvno_type
""); // mnvo_match_data
private class DataConnectionTestHandler extends HandlerThread {
private DataConnectionTestHandler(String name) {
super(name);
}
@Override
public void onLooperPrepared() {
Handler h = new Handler();
mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, h.getLooper(), "");
mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
mDcTesterFailBringUpAll, mDcc, true);
}
}
private void setSuccessfulSetupDataResponse(int cid) {
doAnswer(invocation -> {
final Message msg = (Message) invocation.getArguments()[10];
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1L)
.setId(cid)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
.setInterfaceName("ifname")
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
InetAddresses.parseNumericAddress("fd00:976a::9")))
.setGatewayAddresses(Arrays.asList(
InetAddresses.parseNumericAddress("10.0.2.15"),
InetAddresses.parseNumericAddress("fe80::2")))
.setPcscfAddresses(Arrays.asList(
InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
.setMtu(1500)
.setMtuV4(1500)
.setMtuV6(1500)
.setPduSessionId(1)
.setQosBearerSessions(new ArrayList<>())
.setTrafficDescriptors(new ArrayList<>())
.build();
msg.getData().putParcelable("data_call_response", response);
msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
msg.sendToTarget();
return null;
}).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
any(Message.class));
}
private void setFailedSetupDataResponse(@DataServiceCallback.ResultCode int resultCode) {
doAnswer(invocation -> {
final Message msg = (Message) invocation.getArguments()[10];
msg.arg1 = resultCode;
msg.sendToTarget();
return null;
}).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
any(Message.class));
}
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
logd("+Setup!");
doReturn("fake.action_detached").when(mPhone).getActionDetached();
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
replaceInstance(ConnectionParams.class, "mRilRat", mCp,
ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
doReturn(mApn1).when(mApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_DEFAULT_STRING).when(mApnContext).getApnType();
doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
mDcFailBringUp.saveParameters(0, 0, -2);
doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
mContextFixture.putStringArrayResource(com.android.internal.R.array
.config_mobile_tcp_buffers, new String[]{
"umts:131072,262144,1452032,4096,16384,399360",
"hspa:131072,262144,2441216,4096,16384,399360",
"hsupa:131072,262144,2441216,4096,16384,399360",
"hsdpa:131072,262144,2441216,4096,16384,399360",
"hspap:131072,262144,2441216,4096,16384,399360",
"edge:16384,32768,131072,4096,16384,65536",
"gprs:4096,8192,24576,4096,8192,24576",
"1xrtt:16384,32768,131070,4096,16384,102400",
"evdo:131072,262144,1048576,4096,16384,524288",
"lte:524288,1048576,8388608,262144,524288,4194304"});
mContextFixture.putResource(R.string.config_wwan_data_service_package,
"com.android.phone");
mDcp.mApnContext = mApnContext;
setSuccessfulSetupDataResponse(DEFAULT_DC_CID);
doAnswer(invocation -> {
final Message msg = (Message) invocation.getArguments()[2];
msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
msg.sendToTarget();
return null;
}).when(mDataServiceManager).deactivateDataCall(anyInt(), anyInt(), any(Message.class));
doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mDataServiceManager)
.getTransportType();
mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
mDataConnectionTestHandler.start();
waitForMs(200);
mDc.setDataCallSessionStats(mDataCallSessionStats);
logd("-Setup!");
}
@After
public void tearDown() throws Exception {
logd("tearDown");
mDc = null;
mDcc = null;
mDataConnectionTestHandler.quit();
mDataConnectionTestHandler.join();
super.tearDown();
}
private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
Class[] cArgs = new Class[1];
cArgs[0] = DataCallResponse.class;
Method method = DataConnection.class.getDeclaredMethod("getSuggestedRetryDelay", cArgs);
method.setAccessible(true);
return (long) method.invoke(mDc, response);
}
private boolean isUnmeteredUseOnly() throws Exception {
Method method = DataConnection.class.getDeclaredMethod("isUnmeteredUseOnly");
method.setAccessible(true);
return (boolean) method.invoke(mDc);
}
private boolean isEnterpriseUse() throws Exception {
Method method = DataConnection.class.getDeclaredMethod("isEnterpriseUse");
method.setAccessible(true);
return (boolean) method.invoke(mDc);
}
private boolean isSuspended() throws Exception {
Field field = DataConnection.class.getDeclaredField("mIsSuspended");
field.setAccessible(true);
return field.getBoolean(mDc);
}
private SetupResult setLinkProperties(DataCallResponse response, LinkProperties linkProperties)
throws Exception {
Class[] cArgs = new Class[2];
cArgs[0] = DataCallResponse.class;
cArgs[1] = LinkProperties.class;
Method method = DataConnection.class.getDeclaredMethod("setLinkProperties", cArgs);
method.setAccessible(true);
return (SetupResult) method.invoke(mDc, response, linkProperties);
}
@Test
@SmallTest
public void testConnectEvent() {
assertTrue(mDc.isInactive());
connectEvent(true);
verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
verify(mSimulatedCommandsVerifier, times(1))
.registerForNattKeepaliveStatus(any(Handler.class),
eq(DataConnection.EVENT_KEEPALIVE_STATUS), eq(null));
verify(mSimulatedCommandsVerifier, times(1))
.registerForLceInfo(any(Handler.class),
eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
verify(mVcnManager, atLeastOnce())
.applyVcnNetworkPolicy(
argThat(caps ->
caps.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
any());
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
ArgumentCaptor<TrafficDescriptor> tdCaptor =
ArgumentCaptor.forClass(TrafficDescriptor.class);
verify(mDataServiceManager, times(1)).setupDataCall(
eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
verify(mSimulatedCommandsVerifier, times(1))
.allocatePduSessionId(any());
assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
if (tdCaptor.getValue() != null) {
if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
assertEquals(null, tdCaptor.getValue().getDataNetworkName());
assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
tdCaptor.getValue().getOsAppId()));
} else {
assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
assertEquals(null, tdCaptor.getValue().getOsAppId());
}
}
assertTrue(mDc.isActive());
assertEquals(mDc.getPduSessionId(), 1);
assertEquals(3, mDc.getPcscfAddresses().length);
assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::5"::equals));
}
@Test
public void testConnectEventDuplicateContextIds() throws Exception {
setUpDefaultData(DEFAULT_DC_CID);
// Try to connect ENTERPRISE with the same CID as default
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
// Verify that ENTERPRISE wasn't set up
connectEvent(false);
assertTrue(mDc.isInactive());
// Change the CID
setSuccessfulSetupDataResponse(DEFAULT_DC_CID + 1);
// Verify that ENTERPRISE was set up
connectEvent(true);
assertTrue(mDc.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
@Test
public void testConnectEventNoDefaultData() throws Exception {
assertFalse(mDefaultDc.isActive());
// Try to connect ENTERPRISE when default data doesn't exist
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
// Verify that ENTERPRISE wasn't set up
connectEvent(false);
assertTrue(mDc.isInactive());
// Set up default data
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
setUpDefaultData(1);
// Verify that ENTERPRISE was set up
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
connectEvent(true);
assertTrue(mDc.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
private void setUpDefaultData(int cid) throws Exception {
replaceInstance(DataConnection.class, "mCid", mDefaultDc, cid);
doReturn(true).when(mDefaultDc).isActive();
doReturn(Arrays.asList(mApnContext)).when(mDefaultDc).getApnContexts();
mDcc.addActiveDcByCid(mDefaultDc);
assertTrue(mDefaultDc.getApnContexts().stream()
.anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
}
@Test
@SmallTest
public void testDisconnectEvent() {
testConnectEvent();
mDc.setPduSessionId(5);
disconnectEvent();
verify(mSimulatedCommandsVerifier, times(1)).unregisterForLceInfo(any(Handler.class));
verify(mSimulatedCommandsVerifier, times(1))
.unregisterForNattKeepaliveStatus(any(Handler.class));
verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
verify(mSimulatedCommandsVerifier, times(1))
.releasePduSessionId(any(), eq(5));
assertTrue(mDc.isInactive());
}
@Test
@SmallTest
public void testModemSuggestRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(0)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(1000)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(9999)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
}
@Test
@SmallTest
public void testModemNotSuggestRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-5)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(Long.MIN_VALUE)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
}
@Test
@SmallTest
public void testModemSuggestNoRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(Long.MAX_VALUE)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
}
private NetworkCapabilities getNetworkCapabilities() throws Exception {
Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
method.setAccessible(true);
return (NetworkCapabilities) method.invoke(mDc);
}
private int getDisallowedApnTypes() throws Exception {
Method method = DataConnection.class.getDeclaredMethod("getDisallowedApnTypes");
method.setAccessible(true);
return (int) method.invoke(mDc);
}
@Test
@SmallTest
public void testNetworkCapability() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
doReturn(mApn2).when(mApnContext).getApnSetting();
testConnectEvent();
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
new String[] {"supl"});
disconnectEvent();
doReturn(mApn1).when(mApnContext).getApnSetting();
connectEvent(true);
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
@Test
@SmallTest
public void testEnterpriseNetworkCapability() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
doReturn(mApn2).when(mApnContext).getApnSetting();
testConnectEvent();
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
disconnectEvent();
setUpDefaultData(1);
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
connectEvent(true);
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
@Test
@SmallTest
public void testMeteredCapability() throws Exception {
mContextFixture.getCarrierConfigBundle().
putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] {"default"});
testConnectEvent();
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
}
@Test
@SmallTest
public void testNonMeteredCapability() throws Exception {
doReturn(2819).when(mPhone).getSubId();
mContextFixture.getCarrierConfigBundle().
putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] {"mms"});
testConnectEvent();
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
}
@Test
public void testOverrideUnmetered() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
testConnectEvent();
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
mDc.onMeterednessChanged(true);
waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
mDc.onMeterednessChanged(false);
waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
@Test
public void testOverrideCongested() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
testConnectEvent();
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
mDc.onCongestednessChanged(true);
waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
mDc.onCongestednessChanged(false);
waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
@Test
public void testSubscriptionIds() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
testConnectEvent();
assertEquals(Collections.singleton(0), getNetworkCapabilities().getSubscriptionIds());
}
@Test
public void testShouldSkip464Xlat() throws Exception {
assertFalse(testShouldSkip464XlatEvent(mApn1));
disconnectEvent();
assertTrue(testShouldSkip464XlatEvent(mApn3));
disconnectEvent();
assertTrue(testShouldSkip464XlatEvent(mApn4));
disconnectEvent();
assertFalse(testShouldSkip464XlatEvent(mApn5));
disconnectEvent();
}
private boolean testShouldSkip464XlatEvent(ApnSetting apn) throws Exception {
Method method = DataConnection.class.getDeclaredMethod("shouldSkip464Xlat");
method.setAccessible(true);
doReturn(apn).when(mApnContext).getApnSetting();
doReturn(apn.getApnTypeBitmask()).when(mApnContext).getApnTypeBitmask();
connectEvent(true);
logd(getNetworkCapabilities().toString());
return (Boolean) method.invoke(mDc);
}
private void connectEvent(boolean validate) {
mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
waitForMs(200);
if (validate) {
assertTrue(mDc.isActive());
}
}
private void disconnectEvent() {
mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
waitForMs(100);
assertTrue(mDc.isInactive());
}
private void serviceStateChangedEvent(@RegState int dataRegState, @RilRadioTechnology int rat) {
mDc.obtainMessage(DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED,
new AsyncResult(null, new Pair<>(dataRegState, rat), null)).sendToTarget();
waitForMs(100);
}
@Test
@SmallTest
public void testIsIpAddress() {
// IPv4
assertTrue(DataConnection.isIpAddress("1.2.3.4"));
assertTrue(DataConnection.isIpAddress("127.0.0.1"));
// IPv6
assertTrue(DataConnection.isIpAddress("::1"));
assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
}
@Test
@SmallTest
public void testSetLinkProperties() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
LinkProperties linkProperties = new LinkProperties();
assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
logd(linkProperties.toString());
assertEquals(response.getInterfaceName(), linkProperties.getInterfaceName());
assertEquals(response.getAddresses().size(), linkProperties.getAddresses().size());
for (int i = 0; i < response.getAddresses().size(); ++i) {
assertEquals(response.getAddresses().get(i).getAddress(),
InetAddresses.parseNumericAddress(linkProperties.getLinkAddresses().get(i)
.getAddress().getHostAddress()));
}
assertEquals(response.getDnsAddresses().size(), linkProperties.getDnsServers().size());
for (int i = 0; i < response.getDnsAddresses().size(); ++i) {
assertEquals("i = " + i, response.getDnsAddresses().get(i),
InetAddresses.parseNumericAddress(
linkProperties.getDnsServers().get(i).getHostAddress()));
}
assertEquals(response.getGatewayAddresses().size(), linkProperties.getRoutes().size());
for (int i = 0; i < response.getGatewayAddresses().size(); ++i) {
assertEquals("i = " + i, response.getGatewayAddresses().get(i),
InetAddresses.parseNumericAddress(linkProperties.getRoutes().get(i)
.getGateway().getHostAddress()));
}
assertEquals(response.getPcscfAddresses().size(), linkProperties.getPcscfServers().size());
for (int i = 0; i < response.getPcscfAddresses().size(); ++i) {
assertEquals("i = " + i, response.getPcscfAddresses().get(i),
InetAddresses.parseNumericAddress(linkProperties.getPcscfServers().get(i)
.getHostAddress()));
}
assertEquals(response.getMtu(), linkProperties.getMtu());
}
@Test
@SmallTest
public void testSetLinkPropertiesEmptyAddress() throws Exception {
// 224.224.224.224 is an invalid address.
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
LinkProperties linkProperties = new LinkProperties();
assertEquals(SetupResult.ERROR_INVALID_ARG, setLinkProperties(response, linkProperties));
}
@Test
@SmallTest
public void testSetLinkPropertiesEmptyDns() throws Exception {
// Empty dns entry.
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
.setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
.setInterfaceName(FAKE_IFNAME)
.setAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
.setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
.setPcscfAddresses(
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
.build();
// Make sure no exception was thrown
LinkProperties linkProperties = new LinkProperties();
assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
}
@Test
@SmallTest
public void testStartKeepaliveWLAN() throws Exception {
testConnectEvent();
waitForMs(200);
Field field = DataConnection.class.getDeclaredField("mTransportType");
field.setAccessible(true);
field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
final int sessionHandle = 0xF00;
final int slotId = 3;
final int interval = 10; // seconds
// Construct a new KeepalivePacketData request as we would receive from a Network Agent,
// and check that the packet is sent to the RIL.
KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
InetAddresses.parseNumericAddress("1.2.3.4"),
1234,
InetAddresses.parseNumericAddress("8.8.8.8"),
4500);
mDc.obtainMessage(
DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
waitForMs(100);
// testStartStopNattKeepalive() verifies that this request is passed with WWAN.
// Thus, even though we can't see the response in NetworkAgent, we can verify that the
// CommandsInterface never receives a request and infer that it was dropped due to WLAN.
verify(mSimulatedCommandsVerifier, times(0))
.startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
}
public void checkStartStopNattKeepalive(boolean useCondensedFlow) throws Exception {
testConnectEvent();
waitForMs(200);
final int sessionHandle = 0xF00;
final int slotId = 3;
final int interval = 10; // seconds
// Construct a new KeepalivePacketData request as we would receive from a Network Agent,
// and check that the packet is sent to the RIL.
KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
InetAddresses.parseNumericAddress("1.2.3.4"),
1234,
InetAddresses.parseNumericAddress("8.8.8.8"),
4500);
mDc.obtainMessage(
DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
waitForMs(100);
verify(mSimulatedCommandsVerifier, times(1))
.startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
if (useCondensedFlow) {
// Send a singled condensed response that a keepalive have been requested and the
// activation is completed. This flow should be used if the keepalive offload request
// is handled by a high-priority signalling path.
AsyncResult.forMessage(
kaStarted, new KeepaliveStatus(
sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
kaStarted.sendToTarget();
} else {
// Send the sequential responses indicating first that the request was received and
// then that the keepalive is running. This should create an active record of the
// keepalive in DataConnection while permitting the status from a low priority or other
// high-latency handler to activate the keepalive without blocking a request.
AsyncResult.forMessage(
kaStarted, new KeepaliveStatus(
sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
kaStarted.sendToTarget();
Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
AsyncResult.forMessage(
kaRunning, new KeepaliveStatus(
sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
kaRunning.sendToTarget();
}
waitForMs(100);
// Verify that we can stop the connection, which checks that the record in DataConnection
// has a valid mapping between slotId (from network agent) to sessionHandle (from Radio).
mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
waitForMs(100);
verify(mSimulatedCommandsVerifier, times(1))
.stopNattKeepalive(eq(sessionHandle), any(Message.class));
Message kaStopped = mDc.obtainMessage(
DataConnection.EVENT_KEEPALIVE_STOPPED, sessionHandle, slotId);
AsyncResult.forMessage(kaStopped);
kaStopped.sendToTarget();
// Verify that after the connection is stopped, the mapping for a Keepalive Session is
// removed. Thus, subsequent calls to stop the same keepalive are ignored.
mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
waitForMs(100);
// Check that the mock has not been called subsequent to the previous invocation
// while avoiding the use of reset()
verify(mSimulatedCommandsVerifier, times(1))
.stopNattKeepalive(anyInt(), any(Message.class));
}
@Test
@MediumTest
public void testStartStopNattKeepalive() throws Exception {
checkStartStopNattKeepalive(false);
}
@Test
@MediumTest
public void testStartStopNattKeepaliveCondensed() throws Exception {
checkStartStopNattKeepalive(true);
}
public void checkStartNattKeepaliveFail(boolean useCondensedFlow) throws Exception {
testConnectEvent();
waitForMs(200);
final int sessionHandle = 0xF00;
final int slotId = 3;
final int interval = 10; // seconds
// Construct a new KeepalivePacketData request as we would receive from a Network Agent,
// and check that the packet is sent to the RIL.
KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
InetAddresses.parseNumericAddress("1.2.3.4"),
1234,
InetAddresses.parseNumericAddress("8.8.8.8"),
4500);
mDc.obtainMessage(
DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
waitForMs(100);
verify(mSimulatedCommandsVerifier, times(1))
.startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
if (useCondensedFlow) {
// Indicate in the response that the keepalive has failed.
AsyncResult.forMessage(
kaStarted, new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED), null);
kaStarted.sendToTarget();
} else {
// Indicate that the keepalive is queued, and then signal a failure from the modem
// such that a pending keepalive fails to activate.
AsyncResult.forMessage(
kaStarted, new KeepaliveStatus(
sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
kaStarted.sendToTarget();
Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
AsyncResult.forMessage(
kaRunning, new KeepaliveStatus(
sessionHandle, KeepaliveStatus.STATUS_INACTIVE), null);
kaRunning.sendToTarget();
}
waitForMs(100);
// Verify that a failed connection request cannot be stopped due to no record in
// the DataConnection.
mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
waitForMs(100);
verify(mSimulatedCommandsVerifier, times(0))
.stopNattKeepalive(anyInt(), any(Message.class));
}
@Test
@SmallTest
public void testStartNattKeepaliveFail() throws Exception {
checkStartNattKeepaliveFail(false);
}
@Test
@SmallTest
public void testStartNattKeepaliveFailCondensed() throws Exception {
checkStartNattKeepaliveFail(true);
}
@Test
@SmallTest
public void testIsUnmeteredUseOnly() throws Exception {
Field field = DataConnection.class.getDeclaredField("mTransportType");
field.setAccessible(true);
field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
assertFalse(isUnmeteredUseOnly());
field = DataConnection.class.getDeclaredField("mTransportType");
field.setAccessible(true);
field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(ApnSetting.TYPE_MMS).when(mApnContext).getApnTypeBitmask();
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[] { "default" });
assertTrue(isUnmeteredUseOnly());
}
@Test
public void testIsEnterpriseUse() throws Exception {
assertFalse(isEnterpriseUse());
assertFalse(mDc.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
setUpDefaultData(1);
replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
connectEvent(true);
assertTrue(isEnterpriseUse());
assertTrue(mDc.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
@Test
@SmallTest
public void testGetDisallowedApnTypes() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
new String[] { "mms", "supl", "fota" });
testConnectEvent();
assertEquals(ApnSetting.TYPE_MMS | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA,
getDisallowedApnTypes());
}
@Test
public void testIsSuspended() throws Exception {
// Return false if not active state
assertTrue(mDc.isInactive());
assertFalse(isSuspended());
// Return false for emergency APN
doReturn(mApn6).when(mApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_EMERGENCY).when(mApnContext).getApnTypeBitmask();
connectEvent(true);
assertFalse(isSuspended());
// Back to DEFAULT APN
disconnectEvent();
assertTrue(mDc.isInactive());
doReturn(mApn1).when(mApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
connectEvent(true);
// Before getting any service state event, the connection should not be suspended.
assertFalse(isSuspended());
// Return true if combined reg state is not in service
serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
assertTrue(isSuspended());
// Return false if in service and concurrent voice and data is allowed
serviceStateChangedEvent(ServiceState.STATE_IN_SERVICE,
ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
assertFalse(isSuspended());
// Return false if in service and concurrent voice/data not allowed but call state is idle
doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed();
doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
waitForMs(100);
assertFalse(isSuspended());
// Return true if in service, concurrent voice/data not allowed, and call state not idle
doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
waitForMs(100);
assertTrue(isSuspended());
}
@Test
public void testDataCreatedWhenOutOfService() throws Exception {
serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
ArgumentCaptor<NetworkCapabilities> ncCaptor =
ArgumentCaptor.forClass(NetworkCapabilities.class);
doReturn(mock(Network.class)).when(mConnectivityManager).registerNetworkAgent(
any(), any(), any(), ncCaptor.capture(), any(), any(), anyInt());
doReturn(mApn1).when(mApnContext).getApnSetting();
doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
connectEvent(true);
waitForMs(100);
NetworkCapabilities nc = ncCaptor.getValue();
// The network must be created with NOT_SUSPENDED capability.
assertTrue(nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED));
// But it's final state must be suspended.
assertTrue(isSuspended());
}
@Test
public void testDataServiceTempUnavailable() throws Exception {
setFailedSetupDataResponse(DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
replaceInstance(ConnectionParams.class, "mRequestType", mCp,
DcTracker.REQUEST_TYPE_NORMAL);
// Verify that no data was setup
connectEvent(false);
assertTrue(mDc.isInactive());
// Verify that data service did not suggest any retry (i.e. Frameworks uses configured
// retry timer).
verify(mDataThrottler).setRetryTime(eq(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL),
eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), eq(DcTracker.REQUEST_TYPE_NORMAL));
}
@Test
public void testDataHandoverFailed() throws Exception {
doReturn(mDefaultDc).when(mDcTracker).getDataConnectionByApnType(anyString());
doAnswer(invocation -> {
final Consumer<Integer> consumer = (Consumer<Integer>) invocation.getArguments()[0];
consumer.accept(DataServiceCallback.RESULT_SUCCESS);
return null;
}).when(mDefaultDc).startHandover(any(Consumer.class));
replaceInstance(ConnectionParams.class, "mRequestType", mCp,
DcTracker.REQUEST_TYPE_HANDOVER);
assertTrue(mDc.isInactive());
connectEvent(false);
// Make sure the data connection is still in inactive state
assertTrue(mDc.isInactive());
}
}