Merge "Modify Nsd{Service,Manager}Test to conform to its change"
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index eecd12c..7294ec9 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -2369,7 +2369,7 @@
throw new IllegalArgumentException("Listener was not registered.");
}
try {
- mService.registerNetworkActivityListener(rl);
+ mService.unregisterNetworkActivityListener(rl);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 17fc3b4..ae5cfda 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1055,6 +1055,14 @@
return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
+ /**
+ * Returns true iff this NetworkCapabilities has the specified transport and no other.
+ * @hide
+ */
+ public boolean hasSingleTransport(@Transport int transportType) {
+ return mTransportTypes == (1 << transportType);
+ }
+
private void combineTransportTypes(NetworkCapabilities nc) {
this.mTransportTypes |= nc.mTransportTypes;
}
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index b22457a..f8f86a2 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -125,4 +125,15 @@
details on what is happening. -->
<bool name="config_partialConnectivityNotifiedAsNoInternet">false</bool>
+ <!-- Whether the cell radio of the device is capable of timesharing.
+
+ Whether the cell radio is capable of timesharing between two different networks
+ even for a few seconds. When this is false, the networking stack will ask telephony
+ networks to disconnect immediately, instead of lingering, when outscored by some
+ other telephony network (typically on another subscription). This deprives apps
+ of a chance to gracefully migrate to the new network and degrades the experience
+ for apps, so it should only be set to false when timesharing on the cell radio has
+ extreme adverse effects on performance of the new network.
+ -->
+ <bool translatable="false" name="config_cellular_radio_timesharing_capable">true</bool>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 5af13d7..e5010d7 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -36,6 +36,7 @@
<item type="bool" name="config_partialConnectivityNotifiedAsNoInternet"/>
<item type="drawable" name="stat_notify_wifi_in_range"/>
<item type="drawable" name="stat_notify_rssi_in_range"/>
+ <item type="bool" name="config_cellular_radio_timesharing_capable" />
</policy>
</overlayable>
</resources>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 9f63191..0e05ef2 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -80,6 +80,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
+import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
@@ -335,6 +336,9 @@
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
@VisibleForTesting
protected int mNascentDelayMs;
+ // True if the cell radio of the device is capable of time-sharing.
+ @VisibleForTesting
+ protected boolean mCellularRadioTimesharingCapable = true;
// How long to delay to removal of a pending intent based request.
// See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -1375,6 +1379,12 @@
NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
NetworkRequest.Type.BACKGROUND_REQUEST);
+ mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
+ mCellularRadioTimesharingCapable =
+ mResources.get().getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1385,10 +1395,6 @@
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
- mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- // TODO: Consider making the timer customizable.
- mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
-
mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
@@ -6895,7 +6901,11 @@
if (DBG) {
log("unregister offer from providerId " + noi.offer.providerId + " : " + noi.offer);
}
- mNetworkOffers.remove(noi);
+
+ // If the provider removes the offer and dies immediately afterwards this
+ // function may be called twice in a row, but the array will no longer contain
+ // the offer.
+ if (!mNetworkOffers.remove(noi)) return;
noi.offer.callback.asBinder().unlinkToDeath(noi, 0 /* flags */);
}
@@ -7824,6 +7834,30 @@
bundle.putParcelable(t.getClass().getSimpleName(), t);
}
+ /**
+ * Returns whether reassigning a request from an NAI to another can be done gracefully.
+ *
+ * When a request should be assigned to a new network, it is normally lingered to give
+ * time for apps to gracefully migrate their connections. When both networks are on the same
+ * radio, but that radio can't do time-sharing efficiently, this may end up being
+ * counter-productive because any traffic on the old network may drastically reduce the
+ * performance of the new network.
+ * The stack supports a configuration to let modem vendors state that their radio can't
+ * do time-sharing efficiently. If this configuration is set, the stack assumes moving
+ * from one cell network to another can't be done gracefully.
+ *
+ * @param oldNai the old network serving the request
+ * @param newNai the new network serving the request
+ * @return whether the switch can be graceful
+ */
+ private boolean canSupportGracefulNetworkSwitch(@NonNull final NetworkAgentInfo oldSatisfier,
+ @NonNull final NetworkAgentInfo newSatisfier) {
+ if (mCellularRadioTimesharingCapable) return true;
+ return !oldSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY);
+ }
+
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
@@ -8084,7 +8118,13 @@
log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(previousRequest.requestId);
- previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ if (canSupportGracefulNetworkSwitch(previousSatisfier, newSatisfier)) {
+ // If this network switch can't be supported gracefully, the request is not
+ // lingered. This allows letting go of the network sooner to reclaim some
+ // performance on the new network, since the radio can't do both at the same
+ // time while preserving good performance.
+ previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ }
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index a470c82..f5c43d6 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -119,10 +119,8 @@
import android.net.NetworkInfo.State;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
-import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkUtils;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.SocketKeepalive;
@@ -260,6 +258,7 @@
"config_allowedUnprivilegedKeepalivePerUid";
private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME =
"config_reservedPrivilegedKeepaliveSlots";
+ private static final String TEST_RESTRICTED_NW_IFACE_NAME = "test-restricted-nw";
private static final LinkAddress TEST_LINKADDR = new LinkAddress(
InetAddresses.parseNumericAddress("2001:db8::8"), 64);
@@ -1753,6 +1752,40 @@
greater >= lesser);
}
+ private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
+ final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS,
+ // CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
+ // have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
+ CONNECTIVITY_INTERNAL);
+
+ // Create a restricted network and ensure this package cannot bind to that network either.
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
+ final Network network = agent.getNetwork();
+
+ try (Socket socket = new Socket()) {
+ // Verify that the network is restricted.
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
+ // does not allow to bind socket to restricted network.
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ } finally {
+ agent.unregister();
+ }
+ }
+
/**
* Verifies that apps are not allowed to access restricted networks even if they declare the
* CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
@@ -1769,23 +1802,33 @@
assertTrue(index >= 0);
assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
- // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
- // not have netd system permission to call this function.
- final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+ if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ // Expect binding to the wifi network to succeed.
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ try (Socket socket = new Socket()) {
+ wifiNetwork.bindSocket(socket);
+ }
+ }
// Ensure that this package cannot bind to any restricted network that's currently
// connected.
Network[] networks = mCm.getAllNetworks();
for (Network network : networks) {
- NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
- if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- try {
- network.bindSocket(new Socket());
- fail("Bind to restricted network " + network + " unexpectedly succeeded");
- } catch (IOException expected) {}
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc == null) {
+ continue;
+ }
+
+ try (Socket socket = new Socket()) {
+ if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ network.bindSocket(socket); // binding should succeed
+ } else {
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ }
}
}
+
+ verifyBindSocketToRestrictedNetworkDisallowed();
}
/**
@@ -2840,6 +2883,24 @@
}
}
+ private static NetworkAgent createRestrictedNetworkAgent(final Context context) {
+ // Create test network agent with restricted network.
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ final NetworkAgent agent = new NetworkAgent(context, Looper.getMainLooper(), TAG, nc,
+ new LinkProperties(), 10 /* score */, new NetworkAgentConfig.Builder().build(),
+ new NetworkProvider(context, Looper.getMainLooper(), TAG)) {};
+ runWithShellPermissionIdentity(() -> agent.register(),
+ android.Manifest.permission.MANAGE_TEST_NETWORKS);
+ agent.markConnected();
+
+ return agent;
+ }
+
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
@@ -2860,42 +2921,27 @@
ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
- final Handler h = new Handler(Looper.getMainLooper());
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
- registerBestMatchingNetworkCallback(new NetworkRequest.Builder().clearCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), testNetworkCb, h);
-
- // Create test network agent with restricted network.
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- final NetworkScore score = new NetworkScore.Builder()
- .setExiting(false)
- .setTransportPrimary(false)
- .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
- .build();
- final NetworkAgent agent = new NetworkAgent(mContext, Looper.getMainLooper(),
- TAG, nc, new LinkProperties(), score, new NetworkAgentConfig.Builder().build(),
- new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
- runWithShellPermissionIdentity(() -> agent.register(),
- android.Manifest.permission.MANAGE_TEST_NETWORKS);
- agent.markConnected();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
final Network network = agent.getNetwork();
try (Socket socket = new Socket()) {
- testNetworkCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> network.equals(entry.getNetwork()));
// Verify that the network is restricted.
- final NetworkCapabilities testNetworkNc = mCm.getNetworkCapabilities(network);
- assertNotNull(testNetworkNc);
- assertFalse(testNetworkNc.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 7c380e3..1a131d8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -1072,6 +1072,7 @@
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
+ assertEquals(agent.network, info.getNetwork())
mCM.registerQosCallback(info, executor, qosCallback)
val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 970b7d2..4dc86ff 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -27,6 +27,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -310,6 +311,10 @@
assertTrue(mDisconnected.block(timeoutMs));
}
+ public void assertNotDisconnected(long timeoutMs) {
+ assertFalse(mDisconnected.block(timeoutMs));
+ }
+
public void sendLinkProperties(LinkProperties lp) {
mNetworkAgent.sendLinkProperties(lp);
}
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 49c7271..9ff594a 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -39,6 +39,8 @@
import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT
import android.net.NetworkTemplate.WIFI_NETWORKID_ALL
import android.net.NetworkTemplate.buildTemplateCarrierMetered
+import android.net.NetworkTemplate.buildTemplateMobileAll
+import android.net.NetworkTemplate.buildTemplateMobileWildcard
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
import android.net.NetworkTemplate.buildTemplateWifi
import android.net.NetworkTemplate.buildTemplateWifiWildcard
@@ -171,6 +173,57 @@
}
@Test
+ fun testMobileMatches() {
+ val templateMobileImsi1 = buildTemplateMobileAll(TEST_IMSI1)
+ val templateMobileImsi2WithRatType = buildTemplateMobileWithRatType(TEST_IMSI2,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+
+ val mobileImsi1 = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */,
+ OEM_NONE, true /* metered */)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
+ val identMobile2Umts = buildNetworkIdentity(mockContext, mobileImsi2,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ val identWifiImsi1Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+
+ // Verify that the template matches type and the subscriberId.
+ templateMobileImsi1.assertMatches(identMobile1)
+ templateMobileImsi2WithRatType.assertMatches(identMobile2Umts)
+
+ // Verify that the template does not match the different subscriberId.
+ templateMobileImsi1.assertDoesNotMatch(identMobile2Umts)
+ templateMobileImsi2WithRatType.assertDoesNotMatch(identMobile1)
+
+ // Verify that the different type does not match.
+ templateMobileImsi1.assertDoesNotMatch(identWifiImsi1Ssid1)
+ }
+
+ @Test
+ fun testMobileWildcardMatches() {
+ val templateMobileWildcard = buildTemplateMobileWildcard()
+ val templateMobileNullImsiWithRatType = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+
+ val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Verify that the template matches any subscriberId.
+ templateMobileWildcard.assertMatches(identMobile1)
+ templateMobileNullImsiWithRatType.assertMatches(identMobile1)
+
+ val identWifiImsi1Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+
+ // Verify that the different type does not match.
+ templateMobileWildcard.assertDoesNotMatch(identWifiImsi1Ssid1)
+ templateMobileNullImsiWithRatType.assertDoesNotMatch(identWifiImsi1Ssid1)
+ }
+
+ @Test
fun testCarrierMeteredMatches() {
val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1)
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 7ccd566..1c7ac44 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -350,8 +350,11 @@
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.MockingDetails;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.mockito.exceptions.misusing.UnfinishedStubbingException;
import org.mockito.stubbing.Answer;
import java.io.FileDescriptor;
@@ -1734,7 +1737,15 @@
}
private void returnRealCallingUid() {
- doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
+ try {
+ doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
+ } catch (UnfinishedStubbingException e) {
+ final MockingDetails details = Mockito.mockingDetails(mDeps);
+ Log.e("ConnectivityServiceTest", "UnfinishedStubbingException,"
+ + " Stubbings: " + TextUtils.join(", ", details.getStubbings())
+ + " Invocations: " + details.printInvocations(), e);
+ throw e;
+ }
}
private ConnectivityService.Dependencies makeDependencies() {
@@ -1781,6 +1792,8 @@
doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
.getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ doReturn(true).when(mResources)
+ .getBoolean(R.bool.config_cellular_radio_timesharing_capable);
final ConnectivityResources connRes = mock(ConnectivityResources.class);
doReturn(mResources).when(connRes).get();
@@ -2262,8 +2275,22 @@
// After waitForIdle(), the message was processed and the service didn't crash.
}
+ // TODO : migrate to @Parameterized
@Test
- public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CannotTimeShare() throws Exception {
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(false);
+ }
+
+ public void doTestValidatedCellularOutscoresUnvalidatedWiFi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2297,8 +2324,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CanTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CannotTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(false);
+ }
+
+ public void doTestUnvalidatedWifiOutscoresUnvalidatedCellular(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2323,8 +2363,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnlingeringDoesNotValidate() throws Exception {
+ public void testUnlingeringDoesNotValidate_CanTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnlingeringDoesNotValidate_CannotTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(false);
+ }
+
+ public void doTestUnlingeringDoesNotValidate(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2351,8 +2404,134 @@
NET_CAPABILITY_VALIDATED));
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularOutscoresWeakWifi() throws Exception {
+ public void testRequestMigrationToSameTransport_CanTimeShare() throws Exception {
+ // Simulate a device where the cell radio is capable of time sharing
+ mService.mCellularRadioTimesharingCapable = true;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testRequestMigrationToSameTransport_CannotTimeShare() throws Exception {
+ // Simulate a device where the cell radio is not capable of time sharing
+ mService.mCellularRadioTimesharingCapable = false;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, false);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ public void doTestRequestMigrationToSameTransport(final int transport,
+ final boolean expectLingering) throws Exception {
+ // To speed up tests the linger delay is very short by default in tests but this
+ // test needs to make sure the delay is not incurred so a longer value is safer (it
+ // reduces the risk that a bug exists but goes undetected). The alarm manager in the test
+ // throws and crashes CS if this is set to anything more than the below constant though.
+ mService.mLingerDelayMs = UNREASONABLY_LONG_ALARM_WAIT_MS;
+
+ final TestNetworkCallback generalCb = new TestNetworkCallback();
+ final TestNetworkCallback defaultCb = new TestNetworkCallback();
+ mCm.registerNetworkCallback(
+ new NetworkRequest.Builder().addTransportType(transport | transport).build(),
+ generalCb);
+ mCm.registerDefaultNetworkCallback(defaultCb);
+
+ // Bring up net agent 1
+ final TestNetworkAgentWrapper net1 = new TestNetworkAgentWrapper(transport);
+ net1.connect(true);
+ // Make sure the default request is on net 1
+ generalCb.expectAvailableThenValidatedCallbacks(net1);
+ defaultCb.expectAvailableThenValidatedCallbacks(net1);
+
+ // Bring up net 2 with primary and mms
+ final TestNetworkAgentWrapper net2 = new TestNetworkAgentWrapper(transport);
+ net2.addCapability(NET_CAPABILITY_MMS);
+ net2.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net2.connect(true);
+
+ // Make sure the default request goes to net 2
+ generalCb.expectAvailableCallbacksUnvalidated(net2);
+ if (expectLingering) {
+ generalCb.expectCallback(CallbackEntry.LOSING, net1);
+ }
+ generalCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, net2);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
+
+ // Make sure cell 1 is unwanted immediately if the radio can't time share, but only
+ // after some delay if it can.
+ if (expectLingering) {
+ net1.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // always incurs the timeout
+ generalCb.assertNoCallback();
+ // assertNotDisconnected waited for TEST_CALLBACK_TIMEOUT_MS, so waiting for the
+ // linger period gives TEST_CALLBACK_TIMEOUT_MS time for the event to process.
+ net1.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net1.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net1.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net1);
+
+ // Remove primary from net 2
+ net2.setScore(new NetworkScore.Builder().build());
+ // Request MMS
+ final TestNetworkCallback mmsCallback = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+ mmsCallback);
+ mmsCallback.expectAvailableCallbacksValidated(net2);
+
+ // Bring up net 3 with primary but without MMS
+ final TestNetworkAgentWrapper net3 = new TestNetworkAgentWrapper(transport);
+ net3.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net3.connect(true);
+
+ // Make sure default goes to net 3, but the MMS request doesn't
+ generalCb.expectAvailableThenValidatedCallbacks(net3);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net3);
+ mmsCallback.assertNoCallback();
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // Always incurs the timeout
+
+ // Revoke MMS request and make sure net 2 is torn down with the appropriate delay
+ mCm.unregisterNetworkCallback(mmsCallback);
+ if (expectLingering) {
+ // If the radio can time share, the linger delay hasn't elapsed yet, so apps will
+ // get LOSING. If the radio can't time share, this is a hard loss, since the last
+ // request keeping up this network has been removed and the network isn't lingering
+ // for any other request.
+ generalCb.expectCallback(CallbackEntry.LOSING, net2);
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ generalCb.assertNoCallback();
+ net2.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net2.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net2.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net2);
+ defaultCb.assertNoCallback();
+
+ net3.disconnect();
+ mCm.unregisterNetworkCallback(defaultCb);
+ mCm.unregisterNetworkCallback(generalCb);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestCellularOutscoresWeakWifi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CannotTimeShare() throws Exception {
+ doTestCellularOutscoresWeakWifi(false);
+ }
+
+ public void doTestCellularOutscoresWeakWifi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2377,8 +2556,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testReapingNetwork() throws Exception {
+ public void testReapingNetwork_CanTimeShare() throws Exception {
+ doTestReapingNetwork(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testReapingNetwork_CannotTimeShare() throws Exception {
+ doTestReapingNetwork(false);
+ }
+
+ public void doTestReapingNetwork(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2406,8 +2598,21 @@
mWiFiNetworkAgent.expectDisconnected();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularFallback() throws Exception {
+ public void testCellularFallback_CanTimeShare() throws Exception {
+ doTestCellularFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularFallback_CannotTimeShare() throws Exception {
+ doTestCellularFallback(false);
+ }
+
+ public void doTestCellularFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2444,8 +2649,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testWiFiFallback() throws Exception {
+ public void testWiFiFallback_CanTimeShare() throws Exception {
+ doTestWiFiFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testWiFiFallback_CannotTimeShare() throws Exception {
+ doTestWiFiFallback(false);
+ }
+
+ public void doTestWiFiFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
index e771558..6b4ead5 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -98,14 +98,11 @@
@Before
public void setUp() throws Exception {
sOriginalClock = RecurrenceRule.sClock;
- // ignore any device overlay while testing
- NetworkTemplate.forceAllNetworkTypes();
}
@After
public void tearDown() throws Exception {
RecurrenceRule.sClock = sOriginalClock;
- NetworkTemplate.resetForceAllNetworkTypes();
}
private void setClock(Instant instant) {
@@ -123,7 +120,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -137,7 +134,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
}
@Test
@@ -151,7 +148,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -165,7 +162,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
}
@Test