blob: 881558699260072f3dd22943937ce865b6817ca9 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.telephony.cts;
import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT;
import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_FET;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.SystemProperties;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.mockmodem.MockModemManager;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ApiTest;
import com.android.compatibility.common.util.ShellIdentityUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** Test MockModemService interfaces. */
public class ConnectivityManagerTestOnMockModem {
private static final String TAG = "ConnectivityManagerTestOnMockModem";
private static final int TIMEOUT_NETWORK_VALIDATION = 20000;
private static final int WAIT_MSEC = 500;
private static final int NETWORK_AVAILABLE_SEC = 60;
private static boolean sIsValidate;
private static boolean sIsOnAvailable;
private static Network sDefaultNetwork;
private static Object sIsValidateLock = new Object();
private static Object sIsOnAvailableLock = new Object();
private static CMNetworkCallback sNetworkCallback;
private static MockModemManager sMockModemManager;
private static TelephonyManager sTelephonyManager;
private static SubscriptionManager sSubscriptionManager;
private static ConnectivityManager sConnectivityManager;
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
private static final String RESOURCE_PACKAGE_NAME = "android";
private static boolean sIsMultiSimDevice;
private static class CMNetworkCallback extends NetworkCallback {
final CountDownLatch mNetworkLatch = new CountDownLatch(1);
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
sDefaultNetwork = network;
Log.d(
TAG,
"Network capabilities changed. network: "
+ network
+ ", NetworkCapabilities: "
+ nc);
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
Log.d(
TAG,
"Network capabilities changed. network: "
+ network
+ " ,validation: Pass!");
synchronized (sIsValidateLock) {
sIsValidate = true;
sIsValidateLock.notify();
}
} else {
Log.d(
TAG,
"Network capabilities changed. network: "
+ network
+ " ,validation: Fail!");
synchronized (sIsValidateLock) {
sIsValidate = false;
}
}
}
@Override
public void onLost(Network network) {
sDefaultNetwork = network;
Log.d(TAG, "onLost(): network: " + network);
synchronized (sIsOnAvailableLock) {
sIsOnAvailable = false;
}
}
@Override
public void onAvailable(Network network) {
sDefaultNetwork = network;
Log.d(TAG, "onAvailable(): network: " + network);
synchronized (sIsOnAvailableLock) {
sIsOnAvailable = true;
mNetworkLatch.countDown();
}
}
public void awaitNetwork() throws InterruptedException {
Log.d(TAG, "awaitNetwork(): " + NETWORK_AVAILABLE_SEC + " sec");
mNetworkLatch.await(NETWORK_AVAILABLE_SEC, TimeUnit.SECONDS);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {}
}
@BeforeClass
public static void beforeAllTests() throws Exception {
TimeUnit.SECONDS.sleep(10);
Log.d(TAG, "ConnectivityManagerTestOnMockModem#beforeAllTests()");
if (!hasTelephonyFeature()) {
return;
}
enforceMockModemDeveloperSetting();
sTelephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
sIsMultiSimDevice = isMultiSim(sTelephonyManager);
sConnectivityManager =
(ConnectivityManager) getContext().getSystemService(ConnectivityManager.class);
sSubscriptionManager =
InstrumentationRegistry.getInstrumentation().getContext()
.getSystemService(SubscriptionManager.class);
registerNetworkCallback();
Network activeNetwork = sConnectivityManager.getActiveNetwork();
NetworkCapabilities nc;
if (activeNetwork == null) {
fail("This test requires there is an active network. But the active network is null.");
}
nc = sConnectivityManager.getNetworkCapabilities(activeNetwork);
if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
fail("This test requires there is a transport type with TRANSPORT_CELLULAR.");
}
if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
fail(
"This test requires there is a network capabilities with"
+ " NET_CAPABILITY_INTERNET.");
}
if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
fail(
"This test requires there is a network capabilities with"
+ " NET_CAPABILITY_VALIDATED.");
}
unregisterNetworkCallback();
sMockModemManager = new MockModemManager();
assertNotNull(sMockModemManager);
assertTrue(sMockModemManager.connectMockModemService());
}
@AfterClass
public static void afterAllTests() throws Exception {
Log.d(TAG, "ConnectivityManagerTestOnMockModem#afterAllTests()");
if (!hasTelephonyFeature()) {
return;
}
// Rebind all interfaces which is binding to MockModemService to default.
assertNotNull(sMockModemManager);
assertTrue(sMockModemManager.disconnectMockModemService());
sMockModemManager = null;
}
@Before
public void beforeTest() throws Exception {
assumeTrue(hasTelephonyFeature());
registerNetworkCallback();
}
@After
public void afterTest() {
// unregister the network call back
if (sNetworkCallback != null) {
unregisterNetworkCallback();
}
}
private static boolean isMultiSim(TelephonyManager tm) {
return tm != null && tm.getPhoneCount() > 1;
}
private static boolean isSimHotSwapCapable() {
boolean isSimHotSwapCapable = false;
int resourceId =
getContext()
.getResources()
.getIdentifier("config_hotswapCapable", "bool", RESOURCE_PACKAGE_NAME);
if (resourceId > 0) {
isSimHotSwapCapable = getContext().getResources().getBoolean(resourceId);
} else {
Log.d(TAG, "Fail to get the resource Id, using default.");
}
Log.d(TAG, "isSimHotSwapCapable = " + (isSimHotSwapCapable ? "true" : "false"));
return isSimHotSwapCapable;
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
private static boolean hasTelephonyFeature() {
final PackageManager pm = getContext().getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return false;
}
return true;
}
private static void enforceMockModemDeveloperSetting() throws Exception {
boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
boolean isAllowedForBoot =
SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
// Check for developer settings for user build. Always allow for debug builds
if (!(isAllowed || isAllowedForBoot) && !DEBUG) {
throw new IllegalStateException(
"!! Enable Mock Modem before running this test !! "
+ "Developer options => Allow Mock Modem");
}
}
private int getRegState(int domain) {
int reg;
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity("android.permission.READ_PHONE_STATE");
ServiceState ss = sTelephonyManager.getServiceState();
assertNotNull(ss);
NetworkRegistrationInfo nri =
ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertNotNull(nri);
reg = nri.getRegistrationState();
Log.d(TAG, "SS: " + nri.registrationStateToString(reg));
return reg;
}
private static synchronized boolean getNetworkValidated() {
Log.d(TAG, "getNetworkValidated: " + sIsValidate);
return sIsValidate;
}
private static synchronized boolean getNetworkOnAvailable() {
Log.d(TAG, "getNetworkOnAvailable: " + sIsOnAvailable);
return sIsOnAvailable;
}
private static synchronized Network getDefaultNetwork() {
Log.d(TAG, "getDefaultNetwork: enter ");
return sDefaultNetwork;
}
private static void registerNetworkCallback() {
sNetworkCallback = new CMNetworkCallback();
try {
sConnectivityManager.registerNetworkCallback(
new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(),
sNetworkCallback);
Log.d(TAG, "registered networkCallback");
} catch (RuntimeException e) {
Log.e(TAG, "Exception during registerNetworkCallback():" + e);
}
}
private static void unregisterNetworkCallback() {
try {
sConnectivityManager.unregisterNetworkCallback(sNetworkCallback);
Log.d(TAG, "unregisterNetworkCallback");
} catch (IllegalArgumentException e) {
Log.e(TAG, "IllegalArgumentException during unregisterNetworkCallback(): ", e);
} finally {
sNetworkCallback = null;
}
}
@Test
@ApiTest(
apis = {
"android.net.ConnectivityManager.NetworkCallback#onCapabilitiesChanged",
"android.net.ConnectivityManager.NetworkCallback#onAvailable"
})
public void testNetworkValidated() throws Throwable {
Log.d(TAG, "ConnectivityManagerTestOnMockModem#testNetworkValidated");
assumeTrue(isSimHotSwapCapable());
int slotId = 0;
// Insert a SIM
sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT);
// Enter Service
Log.d(TAG, "testNetworkValidated: Enter Service");
sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, true);
// Get the list of available subscriptions
List<SubscriptionInfo> subscriptionInfoList =
ShellIdentityUtils.invokeMethodWithShellPermissions(
sSubscriptionManager, (sm) -> sm.getActiveSubscriptionInfoList());
Log.d(TAG, "subscriptionInfoList: " + subscriptionInfoList);
for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE");
if (slotId == subscriptionInfo.getSimSlotIndex()) {
sSubscriptionManager.setDefaultDataSubId(subscriptionInfo.getSubscriptionId());
sTelephonyManager.setDataEnabled(subscriptionInfo.getSubscriptionId(), true);
Log.d(TAG, "Set Data on slot: " + slotId);
}
}
// make sure the network is available
sNetworkCallback.awaitNetwork();
assertTrue(getNetworkOnAvailable());
// make sure the network is validated
sConnectivityManager.reportNetworkConnectivity(getDefaultNetwork(), false);
waitForExpectedValidationState(true, TIMEOUT_NETWORK_VALIDATION);
assertTrue(getNetworkValidated());
// Leave Service
Log.d(TAG, "testNetworkValidated: Leave Service");
sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, false);
// Remove the SIM
sMockModemManager.removeSimCard(slotId);
}
private static void waitForExpectedValidationState(boolean validated, long timeout)
throws InterruptedException {
Log.d(
TAG,
"Wait For Expected ValidationState: expected: "
+ validated
+ ", timeout: "
+ timeout
+ "ms");
synchronized (sIsValidateLock) {
long expectedTimeout = System.currentTimeMillis() + timeout;
boolean expectedResult = validated;
while (System.currentTimeMillis() < expectedTimeout
&& getNetworkValidated() != expectedResult) {
sIsValidateLock.wait(WAIT_MSEC);
}
}
}
@Test
public void testDDSChange() throws Throwable {
Log.d(TAG, "ConnectivityManagerTestOnMockModem#testDDSChange");
assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice);
int slotId_0 = 0;
int slotId_1 = 1;
// Insert a SIM
sMockModemManager.insertSimCard(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT);
sMockModemManager.insertSimCard(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET);
// Enter Service
Log.d(TAG, "testDsdsServiceStateChange: Enter Service");
sMockModemManager.changeNetworkService(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT, true);
sMockModemManager.changeNetworkService(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET, true);
for (int slotIndex = 0; slotIndex < 2; slotIndex++) {
boolean isDataEnabled = sTelephonyManager.getDataEnabled(slotIndex);
Log.d(TAG, "Data enabled status for SIM slot " + slotIndex + ": " + isDataEnabled);
}
String packageName = getContext().getPackageName();
Log.d(TAG, "packageName: " + packageName);
// Get the list of available subscriptions
List<SubscriptionInfo> subscriptionInfoList =
ShellIdentityUtils.invokeMethodWithShellPermissions(
sSubscriptionManager, (sm) -> sm.getActiveSubscriptionInfoList());
Log.d(TAG, "subscriptionInfoList: " + subscriptionInfoList);
// Loop through the subscriptions to get the phone id
// Set the data enable by phone id
for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE");
int slotId = subscriptionInfo.getSimSlotIndex();
sSubscriptionManager.setDefaultDataSubId(subscriptionInfo.getSubscriptionId());
sTelephonyManager.setDataEnabled(subscriptionInfo.getSubscriptionId(), true);
Log.d(TAG, "Set Data on slot: " + slotId);
// make sure the network is available
sNetworkCallback.awaitNetwork();
Log.d(TAG, "Check Data : " + getNetworkOnAvailable());
assertTrue(getNetworkOnAvailable());
}
// Leave Service
Log.d(TAG, "testDsdsServiceStateChange: Leave Service");
sMockModemManager.changeNetworkService(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT, false);
sMockModemManager.changeNetworkService(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET, false);
// Remove the SIM
sMockModemManager.removeSimCard(slotId_0);
sMockModemManager.removeSimCard(slotId_1);
}
}