Merge "Small doc improvement"
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..7223c19 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");
@@ -3842,9 +3848,7 @@
private void handleNetworkAgentDisconnected(Message msg) {
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
- if (mNetworkAgentInfos.contains(nai)) {
- disconnectAndDestroyNetwork(nai);
- }
+ disconnectAndDestroyNetwork(nai);
}
// Destroys a network, remove references to it from the internal state managed by
@@ -3852,6 +3856,9 @@
// Must be called on the Handler thread.
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
ensureRunningOnConnectivityServiceThread();
+
+ if (!mNetworkAgentInfos.contains(nai)) return;
+
if (DBG) {
log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
@@ -3937,7 +3944,7 @@
try {
mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
} catch (RemoteException e) {
- Log.d(TAG, "Error marking network restricted during teardown: " + e);
+ Log.d(TAG, "Error marking network restricted during teardown: ", e);
}
mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
}
@@ -6895,7 +6902,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 +7835,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 +8119,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/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/Android.bp b/tests/unit/Android.bp
index 7b23255..4720706 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -66,6 +66,7 @@
"framework-protos",
"mockito-target-minus-junit4",
"net-tests-utils",
+ "platform-compat-test-rules",
"platform-test-annotations",
"service-connectivity-pre-jarjar",
"services.core",
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 31c8927..de77d23 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -16,6 +16,9 @@
package android.net.nsd;
+import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -27,6 +30,7 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -44,7 +48,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -56,6 +62,9 @@
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Mock Context mContext;
@Mock INsdManager mService;
MockServiceHandler mServiceHandler;
@@ -70,8 +79,6 @@
mServiceHandler = spy(MockServiceHandler.create(mContext));
doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger();
-
- mManager = makeManager();
}
@After
@@ -85,7 +92,76 @@
}
@Test
- public void testResolveService() {
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testResolveServiceS() {
+ mManager = makeNsdManagerS();
+ doTestResolveService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testResolveServicePreS() {
+ mManager = makeNsdManagerPreS();
+ doTestResolveService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testDiscoverServiceS() {
+ mManager = makeNsdManagerS();
+ doTestDiscoverService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testDiscoverServicePreS() {
+ mManager = makeNsdManagerPreS();
+ doTestDiscoverService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testParallelResolveServiceS() {
+ mManager = makeNsdManagerS();
+ doTestParallelResolveService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testParallelResolveServicePreS() {
+ mManager = makeNsdManagerPreS();
+ doTestParallelResolveService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testInvalidCallsS() {
+ mManager = makeNsdManagerS();
+ doTestInvalidCalls();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testInvalidCallsPreS() {
+ mManager = makeNsdManagerPreS();
+ doTestInvalidCalls();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testRegisterServiceS() {
+ mManager = makeNsdManagerS();
+ doTestRegisterService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testRegisterServicePreS() {
+ mManager = makeNsdManagerPreS();
+ doTestRegisterService();
+ }
+
+ public void doTestResolveService() {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -104,8 +180,7 @@
verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- @Test
- public void testParallelResolveService() {
+ public void doTestParallelResolveService() {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -127,8 +202,7 @@
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- @Test
- public void testRegisterService() {
+ public void doTestRegisterService() {
NsdManager manager = mManager;
NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
@@ -186,8 +260,7 @@
//verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
}
- @Test
- public void testDiscoverService() {
+ public void doTestDiscoverService() {
NsdManager manager = mManager;
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
@@ -264,8 +337,7 @@
verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
}
- @Test
- public void testInvalidCalls() {
+ public void doTestInvalidCalls() {
NsdManager manager = mManager;
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
@@ -326,10 +398,21 @@
}
}
- NsdManager makeManager() {
+ NsdManager makeNsdManagerS() {
+ // Expect we'll get 2 AsyncChannel related msgs.
+ return makeManager(2);
+ }
+
+ NsdManager makeNsdManagerPreS() {
+ // Expect we'll get 3 msgs. 2 AsyncChannel related msgs + 1 additional daemon startup msg.
+ return makeManager(3);
+ }
+
+ NsdManager makeManager(int expectedMsgCount) {
NsdManager manager = new NsdManager(mContext, mService);
// Acknowledge first two messages connecting the AsyncChannel.
- verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any());
+ verify(mServiceHandler, timeout(mTimeoutMs).times(expectedMsgCount)).handleMessage(any());
+
reset(mServiceHandler);
assertNotNull(mServiceHandler.chan);
return manager;
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 2aba0b4..1c7ac44 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -1792,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();
@@ -2273,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);
@@ -2308,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);
@@ -2334,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);
@@ -2362,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);
@@ -2388,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);
@@ -2417,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);
@@ -2455,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/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index e80a938..4d2970a 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,6 +16,9 @@
package com.android.server;
+import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
@@ -27,6 +30,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.Context;
import android.net.nsd.NsdManager;
@@ -48,7 +52,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -67,6 +73,8 @@
private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext;
@Mock ContentResolver mResolver;
@Mock NsdService.NsdSettings mSettings;
@@ -94,6 +102,34 @@
}
@Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testPreSClients() {
+ when(mSettings.isEnabled()).thenReturn(true);
+ NsdService service = makeService();
+
+ // Pre S client connected, the daemon should be started.
+ NsdManager client1 = connectClient(service);
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
+ verifyDaemonCommands("start-service");
+
+ NsdManager client2 = connectClient(service);
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
+
+ client1.disconnect();
+ // Still 1 client remains, daemon shouldn't be stopped.
+ waitForIdle();
+ verify(mDaemon, never()).maybeStop();
+
+ client2.disconnect();
+ // All clients are disconnected, the daemon should be stopped.
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ verifyDaemonCommands("stop-service");
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testNoDaemonStartedWhenClientsConnect() {
when(mSettings.isEnabled()).thenReturn(true);
@@ -110,13 +146,12 @@
waitForIdle();
verify(mDaemon, never()).execute(any());
+ // If there is no active request, try to clean up the daemon
+ // every time the client disconnects.
client1.disconnect();
- // Still 1 client remains, daemon shouldn't be stopped.
- waitForIdle();
- verify(mDaemon, never()).maybeStop();
-
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ reset(mDaemon);
client2.disconnect();
- // All clients are disconnected, stop the daemon after CLEANUP_DELAY_MS.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
client1.disconnect();
@@ -124,6 +159,7 @@
}
@Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testClientRequestsAreGCedAtDisconnection() {
when(mSettings.isEnabled()).thenReturn(true);
@@ -169,6 +205,7 @@
}
@Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testCleanupDelayNoRequestActive() {
when(mSettings.isEnabled()).thenReturn(true);
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