Merge "Add a NetworkAgent API to indicate that a network will be replaced."
This commit is contained in:
@@ -236,6 +236,7 @@ package android.net {
|
||||
public abstract class NetworkAgent {
|
||||
ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
|
||||
ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
|
||||
method public void destroyAndAwaitReplacement(@IntRange(from=0, to=0x1388) int);
|
||||
method @Nullable public android.net.Network getNetwork();
|
||||
method public void markConnected();
|
||||
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
|
||||
|
||||
@@ -47,4 +47,5 @@ oneway interface INetworkAgentRegistry {
|
||||
void sendAddDscpPolicy(in DscpPolicy policy);
|
||||
void sendRemoveDscpPolicy(int policyId);
|
||||
void sendRemoveAllDscpPolicies();
|
||||
void sendDestroyAndAwaitReplacement(int timeoutMillis);
|
||||
}
|
||||
|
||||
@@ -434,6 +434,14 @@ public abstract class NetworkAgent {
|
||||
*/
|
||||
public static final int CMD_DSCP_POLICY_STATUS = BASE + 28;
|
||||
|
||||
/**
|
||||
* Sent by the NetworkAgent to ConnectivityService to notify that this network is expected to be
|
||||
* replaced within the specified time by a similar network.
|
||||
* arg1 = timeout in milliseconds
|
||||
* @hide
|
||||
*/
|
||||
public static final int EVENT_DESTROY_AND_AWAIT_REPLACEMENT = BASE + 29;
|
||||
|
||||
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
|
||||
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
|
||||
config.legacyTypeName, config.legacySubTypeName);
|
||||
@@ -942,6 +950,45 @@ public abstract class NetworkAgent {
|
||||
queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMillis));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this agent will likely soon be replaced by another agent for a very similar
|
||||
* network (e.g., same Wi-Fi SSID).
|
||||
*
|
||||
* If the network is not currently satisfying any {@link NetworkRequest}s, it will be torn down.
|
||||
* If it is satisfying requests, then the native network corresponding to the agent will be
|
||||
* destroyed immediately, but the agent will remain registered and will continue to satisfy
|
||||
* requests until {@link #unregister} is called, the network is replaced by an equivalent or
|
||||
* better network, or the specified timeout expires. During this time:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The agent may not send any further updates, for example by calling methods
|
||||
* such as {@link #sendNetworkCapabilities}, {@link #sendLinkProperties},
|
||||
* {@link #sendNetworkScore(NetworkScore)} and so on. Any such updates will be ignored.
|
||||
* <li>The network will remain connected and continue to satisfy any requests that it would
|
||||
* otherwise satisfy (including, possibly, the default request).
|
||||
* <li>The validation state of the network will not change, and calls to
|
||||
* {@link ConnectivityManager#reportNetworkConnectivity(Network, boolean)} will be ignored.
|
||||
* </ul>
|
||||
*
|
||||
* Once this method is called, it is not possible to restore the agent to a functioning state.
|
||||
* If a replacement network becomes available, then a new agent must be registered. When that
|
||||
* replacement network is fully capable of replacing this network (including, possibly, being
|
||||
* validated), this agent will no longer be needed and will be torn down. Otherwise, this agent
|
||||
* can be disconnected by calling {@link #unregister}. If {@link #unregister} is not called,
|
||||
* this agent will automatically be unregistered when the specified timeout expires. Any
|
||||
* teardown delay previously set using{@link #setTeardownDelayMillis} is ignored.
|
||||
*
|
||||
* <p>This method has no effect if {@link #markConnected} has not yet been called.
|
||||
* <p>This method may only be called once.
|
||||
*
|
||||
* @param timeoutMillis the timeout after which this network will be unregistered even if
|
||||
* {@link #unregister} was not called.
|
||||
*/
|
||||
public void destroyAndAwaitReplacement(
|
||||
@IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int timeoutMillis) {
|
||||
queueOrSendMessage(reg -> reg.sendDestroyAndAwaitReplacement(timeoutMillis));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the legacy subtype of this network agent.
|
||||
*
|
||||
|
||||
@@ -3503,6 +3503,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isDisconnectRequest(Message msg) {
|
||||
if (msg.what != NetworkAgent.EVENT_NETWORK_INFO_CHANGED) return false;
|
||||
final NetworkInfo info = (NetworkInfo) ((Pair) msg.obj).second;
|
||||
return info.getState() == NetworkInfo.State.DISCONNECTED;
|
||||
}
|
||||
|
||||
// must be stateless - things change under us.
|
||||
private class NetworkStateTrackerHandler extends Handler {
|
||||
public NetworkStateTrackerHandler(Looper looper) {
|
||||
@@ -3519,6 +3525,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
// If the network has been destroyed, the only thing that it can do is disconnect.
|
||||
if (nai.destroyed && !isDisconnectRequest(msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.what) {
|
||||
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
|
||||
final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
|
||||
@@ -3620,12 +3631,60 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetworkAgent.EVENT_DESTROY_AND_AWAIT_REPLACEMENT: {
|
||||
// If nai is not yet created, or is already destroyed, ignore.
|
||||
if (!shouldDestroyNativeNetwork(nai)) break;
|
||||
|
||||
final int timeoutMs = (int) arg.second;
|
||||
if (timeoutMs < 0 || timeoutMs > NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
|
||||
Log.e(TAG, "Invalid network replacement timer " + timeoutMs
|
||||
+ ", must be between 0 and " + NetworkAgent.MAX_TEARDOWN_DELAY_MS);
|
||||
}
|
||||
|
||||
// Marking a network awaiting replacement is used to ensure that any requests
|
||||
// satisfied by the network do not switch to another network until a
|
||||
// replacement is available or the wait for a replacement times out.
|
||||
// If the network is inactive (i.e., nascent or lingering), then there are no
|
||||
// such requests, and there is no point keeping it. Just tear it down.
|
||||
// Note that setLingerDuration(0) cannot be used to do this because the network
|
||||
// could be nascent.
|
||||
nai.clearInactivityState();
|
||||
if (unneeded(nai, UnneededFor.TEARDOWN)) {
|
||||
Log.d(TAG, nai.toShortString()
|
||||
+ " marked awaiting replacement is unneeded, tearing down instead");
|
||||
teardownUnneededNetwork(nai);
|
||||
break;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Marking " + nai.toShortString()
|
||||
+ " destroyed, awaiting replacement within " + timeoutMs + "ms");
|
||||
destroyNativeNetwork(nai);
|
||||
|
||||
// TODO: deduplicate this call with the one in disconnectAndDestroyNetwork.
|
||||
// This is not trivial because KeepaliveTracker#handleStartKeepalive does not
|
||||
// consider the fact that the network could already have disconnected or been
|
||||
// destroyed. Fix the code to send ERROR_INVALID_NETWORK when this happens
|
||||
// (taking care to ensure no dup'd FD leaks), then remove the code duplication
|
||||
// and move this code to a sensible location (destroyNativeNetwork perhaps?).
|
||||
mKeepaliveTracker.handleStopAllKeepalives(nai,
|
||||
SocketKeepalive.ERROR_INVALID_NETWORK);
|
||||
|
||||
nai.updateScoreForNetworkAgentUpdate();
|
||||
// This rematch is almost certainly not going to result in any changes, because
|
||||
// the destroyed flag is only just above the "current satisfier wins"
|
||||
// tie-breaker. But technically anything that affects scoring should rematch.
|
||||
rematchAllNetworksAndRequests();
|
||||
mHandler.postDelayed(() -> nai.disconnect(), timeoutMs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean maybeHandleNetworkMonitorMessage(Message msg) {
|
||||
final int netId = msg.arg2;
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
|
||||
// If a network has already been destroyed, all NetworkMonitor updates are ignored.
|
||||
if (nai != null && nai.destroyed) return true;
|
||||
switch (msg.what) {
|
||||
default:
|
||||
return false;
|
||||
@@ -4125,6 +4184,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldDestroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
|
||||
return nai.created && !nai.destroyed;
|
||||
}
|
||||
|
||||
private void handleNetworkAgentDisconnected(Message msg) {
|
||||
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
|
||||
disconnectAndDestroyNetwork(nai);
|
||||
@@ -4231,7 +4294,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
private void destroyNetwork(NetworkAgentInfo nai) {
|
||||
if (nai.created) {
|
||||
if (shouldDestroyNativeNetwork(nai)) {
|
||||
// Tell netd to clean up the configuration for this network
|
||||
// (routing rules, DNS, etc).
|
||||
// This may be slow as it requires a lot of netd shelling out to ip and
|
||||
@@ -4240,15 +4303,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
// network or service a new request from an app), so network traffic isn't interrupted
|
||||
// for an unnecessarily long time.
|
||||
destroyNativeNetwork(nai);
|
||||
mDnsManager.removeNetwork(nai.network);
|
||||
|
||||
// clean up tc police filters on interface.
|
||||
if (nai.everConnected && canNetworkBeRateLimited(nai) && mIngressRateLimit >= 0) {
|
||||
mDeps.disableIngressRateLimit(nai.linkProperties.getInterfaceName());
|
||||
}
|
||||
if (!nai.created && !SdkLevel.isAtLeastT()) {
|
||||
// Backwards compatibility: send onNetworkDestroyed even if network was never created.
|
||||
// This can never run if the code above runs because shouldDestroyNativeNetwork is
|
||||
// false if the network was never created.
|
||||
// TODO: delete when S is no longer supported.
|
||||
nai.onNetworkDestroyed();
|
||||
}
|
||||
mNetIdManager.releaseNetId(nai.network.getNetId());
|
||||
nai.onNetworkDestroyed();
|
||||
}
|
||||
|
||||
private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) {
|
||||
@@ -4291,6 +4354,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
loge("Exception destroying network: " + e);
|
||||
}
|
||||
// TODO: defer calling this until the network is removed from mNetworkAgentInfos.
|
||||
// Otherwise, a private DNS configuration update for a destroyed network, or one that never
|
||||
// gets created, could add data to DnsManager data structures that will never get deleted.
|
||||
mDnsManager.removeNetwork(nai.network);
|
||||
|
||||
// clean up tc police filters on interface.
|
||||
if (nai.everConnected && canNetworkBeRateLimited(nai) && mIngressRateLimit >= 0) {
|
||||
mDeps.disableIngressRateLimit(nai.linkProperties.getInterfaceName());
|
||||
}
|
||||
|
||||
nai.destroyed = true;
|
||||
nai.onNetworkDestroyed();
|
||||
}
|
||||
|
||||
// If this method proves to be too slow then we can maintain a separate
|
||||
@@ -8543,11 +8618,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
log(" accepting network in place of " + previousSatisfier.toShortString());
|
||||
}
|
||||
previousSatisfier.removeRequest(previousRequest.requestId);
|
||||
if (canSupportGracefulNetworkSwitch(previousSatisfier, newSatisfier)) {
|
||||
if (canSupportGracefulNetworkSwitch(previousSatisfier, newSatisfier)
|
||||
&& !previousSatisfier.destroyed) {
|
||||
// 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.
|
||||
//
|
||||
// Also don't linger the request if the old network has been destroyed.
|
||||
// A destroyed network does not provide actual network connectivity, so
|
||||
// lingering it is not useful. In particular this ensures that a destroyed
|
||||
// network is outscored by its replacement,
|
||||
// then it is torn down immediately instead of being lingered, and any apps that
|
||||
// were using it immediately get onLost and can connect using the new network.
|
||||
previousSatisfier.lingerRequest(previousRequest.requestId, now);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -132,7 +132,7 @@ public class Nat464Xlat {
|
||||
final boolean skip464xlat = (nai.netAgentConfig() != null)
|
||||
&& nai.netAgentConfig().skip464xlat;
|
||||
|
||||
return supported && connected && isIpv6OnlyNetwork && !skip464xlat
|
||||
return supported && connected && isIpv6OnlyNetwork && !skip464xlat && !nai.destroyed
|
||||
&& (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
|
||||
? isCellular464XlatEnabled() : true);
|
||||
}
|
||||
|
||||
@@ -732,6 +732,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
|
||||
mHandler.obtainMessage(NetworkAgent.EVENT_REMOVE_ALL_DSCP_POLICIES,
|
||||
new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDestroyAndAwaitReplacement(final int timeoutMillis) {
|
||||
mHandler.obtainMessage(NetworkAgent.EVENT_DESTROY_AND_AWAIT_REPLACEMENT,
|
||||
new Pair<>(NetworkAgentInfo.this, timeoutMillis)).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -976,7 +982,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
|
||||
/**
|
||||
* Update the ConnectivityService-managed bits in the score.
|
||||
*
|
||||
* Call this after updating the network agent config.
|
||||
* Call this after changing any data that might affect the score (e.g., agent config).
|
||||
*/
|
||||
public void updateScoreForNetworkAgentUpdate() {
|
||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
|
||||
@@ -1256,6 +1262,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
|
||||
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
|
||||
+ networkInfo.toShortString() + "} "
|
||||
+ mScore + " "
|
||||
+ (created ? " created" : "")
|
||||
+ (destroyed ? " destroyed" : "")
|
||||
+ (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
|
||||
+ (everValidated ? " everValidated" : "")
|
||||
+ (lastValidated ? " lastValidated" : "")
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.Manifest.permission.NETWORK_SETTINGS
|
||||
import android.app.Instrumentation
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.EthernetNetworkSpecifier
|
||||
import android.net.INetworkAgent
|
||||
import android.net.INetworkAgentRegistry
|
||||
import android.net.InetAddresses
|
||||
@@ -35,6 +36,7 @@ import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
|
||||
@@ -42,7 +44,9 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.net.NetworkCapabilities.TRANSPORT_TEST
|
||||
import android.net.NetworkCapabilities.TRANSPORT_WIFI
|
||||
import android.net.NetworkCapabilities.TRANSPORT_VPN
|
||||
import android.net.NetworkInfo
|
||||
import android.net.NetworkProvider
|
||||
@@ -100,6 +104,7 @@ import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStopSocketKeep
|
||||
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
|
||||
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
|
||||
import com.android.testutils.TestableNetworkCallback
|
||||
import com.android.testutils.assertThrows
|
||||
import org.junit.After
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.Before
|
||||
@@ -112,6 +117,8 @@ import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.timeout
|
||||
import org.mockito.Mockito.verify
|
||||
import java.io.IOException
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Socket
|
||||
@@ -249,14 +256,10 @@ class NetworkAgentTest {
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createNetworkAgent(
|
||||
context: Context = realContext,
|
||||
private fun makeTestNetworkCapabilities(
|
||||
specifier: String? = null,
|
||||
initialNc: NetworkCapabilities? = null,
|
||||
initialLp: LinkProperties? = null,
|
||||
initialConfig: NetworkAgentConfig? = null
|
||||
): TestableNetworkAgent {
|
||||
val nc = initialNc ?: NetworkCapabilities().apply {
|
||||
transports: IntArray = intArrayOf()
|
||||
) = NetworkCapabilities().apply {
|
||||
addTransportType(TRANSPORT_TEST)
|
||||
removeCapability(NET_CAPABILITY_TRUSTED)
|
||||
removeCapability(NET_CAPABILITY_INTERNET)
|
||||
@@ -269,7 +272,20 @@ class NetworkAgentTest {
|
||||
if (null != specifier) {
|
||||
setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(specifier))
|
||||
}
|
||||
for (t in transports) { addTransportType(t) }
|
||||
// Most transports are not allowed on test networks unless the network is marked restricted.
|
||||
// This test does not need
|
||||
if (transports.size > 0) removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
|
||||
}
|
||||
|
||||
private fun createNetworkAgent(
|
||||
context: Context = realContext,
|
||||
specifier: String? = null,
|
||||
initialNc: NetworkCapabilities? = null,
|
||||
initialLp: LinkProperties? = null,
|
||||
initialConfig: NetworkAgentConfig? = null
|
||||
): TestableNetworkAgent {
|
||||
val nc = initialNc ?: makeTestNetworkCapabilities(specifier)
|
||||
val lp = initialLp ?: LinkProperties().apply {
|
||||
addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
|
||||
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
|
||||
@@ -284,12 +300,14 @@ class NetworkAgentTest {
|
||||
context: Context = realContext,
|
||||
specifier: String? = UUID.randomUUID().toString(),
|
||||
initialConfig: NetworkAgentConfig? = null,
|
||||
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf()
|
||||
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf(),
|
||||
transports: IntArray = intArrayOf()
|
||||
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
|
||||
val callback = TestableNetworkCallback()
|
||||
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
|
||||
requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
|
||||
val agent = createNetworkAgent(context, specifier, initialConfig = initialConfig)
|
||||
val nc = makeTestNetworkCapabilities(specifier, transports)
|
||||
val agent = createNetworkAgent(context, initialConfig = initialConfig, initialNc = nc)
|
||||
agent.setTeardownDelayMillis(0)
|
||||
// Connect the agent and verify initial status callbacks.
|
||||
agent.register()
|
||||
@@ -301,6 +319,15 @@ class NetworkAgentTest {
|
||||
return agent to callback
|
||||
}
|
||||
|
||||
private fun connectNetwork(vararg transports: Int): Pair<TestableNetworkAgent, Network> {
|
||||
val (agent, callback) = createConnectedNetworkAgent(transports = transports)
|
||||
val network = agent.network!!
|
||||
// createConnectedNetworkAgent internally files a request; release it so that the network
|
||||
// will be torn down if unneeded.
|
||||
mCM.unregisterNetworkCallback(callback)
|
||||
return agent to network
|
||||
}
|
||||
|
||||
private fun createNetworkAgentWithFakeCS() = createNetworkAgent().also {
|
||||
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
|
||||
}
|
||||
@@ -1123,4 +1150,138 @@ class NetworkAgentTest {
|
||||
remoteAddresses
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDestroyAndAwaitReplacement() {
|
||||
// Keeps an eye on all test networks.
|
||||
val matchAllCallback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
|
||||
registerNetworkCallback(makeTestNetworkRequest(), matchAllCallback)
|
||||
|
||||
// File a request that matches and keeps up the best-scoring test network.
|
||||
val testCallback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
|
||||
requestNetwork(makeTestNetworkRequest(), testCallback)
|
||||
|
||||
// Connect the first network. This should satisfy the request.
|
||||
val (agent1, network1) = connectNetwork()
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(network1)
|
||||
testCallback.expectAvailableThenValidatedCallbacks(network1)
|
||||
// Check that network1 exists by binding a socket to it and getting no exceptions.
|
||||
network1.bindSocket(DatagramSocket())
|
||||
|
||||
// Connect a second agent. network1 is preferred because it was already registered, so
|
||||
// testCallback will not see any events. agent2 is be torn down because it has no requests.
|
||||
val (agent2, network2) = connectNetwork()
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(network2)
|
||||
matchAllCallback.expectCallback<Lost>(network2)
|
||||
agent2.expectCallback<OnNetworkUnwanted>()
|
||||
agent2.expectCallback<OnNetworkDestroyed>()
|
||||
assertNull(mCM.getLinkProperties(network2))
|
||||
|
||||
// Mark the first network as awaiting replacement. This should destroy the underlying
|
||||
// native network and send onNetworkDestroyed, but will not send any NetworkCallbacks,
|
||||
// because for callback and scoring purposes network1 is still connected.
|
||||
agent1.destroyAndAwaitReplacement(5_000 /* timeoutMillis */)
|
||||
agent1.expectCallback<OnNetworkDestroyed>()
|
||||
assertThrows(IOException::class.java) { network1.bindSocket(DatagramSocket()) }
|
||||
assertNotNull(mCM.getLinkProperties(network1))
|
||||
|
||||
// Calling destroyAndAwaitReplacement more than once has no effect.
|
||||
// If it did, this test would fail because the 1ms timeout means that the network would be
|
||||
// torn down before the replacement arrives.
|
||||
agent1.destroyAndAwaitReplacement(1 /* timeoutMillis */)
|
||||
|
||||
// Connect a third network. Because network1 is awaiting replacement, network3 is preferred
|
||||
// as soon as it validates (until then, it is outscored by network1).
|
||||
// The fact that the first events seen by matchAllCallback is the connection of network3
|
||||
// implicitly ensures that no callbacks are sent since network1 was lost.
|
||||
val (agent3, network3) = connectNetwork()
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(network3)
|
||||
testCallback.expectAvailableDoubleValidatedCallbacks(network3)
|
||||
|
||||
// As soon as the replacement arrives, network1 is disconnected.
|
||||
// Check that this happens before the replacement timeout (5 seconds) fires.
|
||||
matchAllCallback.expectCallback<Lost>(network1, 2_000 /* timeoutMs */)
|
||||
agent1.expectCallback<OnNetworkUnwanted>()
|
||||
|
||||
// Test lingering:
|
||||
// - Connect a higher-scoring network and check that network3 starts lingering.
|
||||
// - Mark network3 awaiting replacement.
|
||||
// - Check that network3 is torn down immediately without waiting for the linger timer or
|
||||
// the replacement timer to fire. This is a regular teardown, so it results in
|
||||
// onNetworkUnwanted before onNetworkDestroyed.
|
||||
val (agent4, agent4callback) = createConnectedNetworkAgent()
|
||||
val network4 = agent4.network!!
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(network4)
|
||||
agent4.sendNetworkScore(NetworkScore.Builder().setTransportPrimary(true).build())
|
||||
matchAllCallback.expectCallback<Losing>(network3)
|
||||
testCallback.expectAvailableCallbacks(network4, validated = true)
|
||||
mCM.unregisterNetworkCallback(agent4callback)
|
||||
agent3.destroyAndAwaitReplacement(5_000)
|
||||
agent3.expectCallback<OnNetworkUnwanted>()
|
||||
matchAllCallback.expectCallback<Lost>(network3, 1000L)
|
||||
agent3.expectCallback<OnNetworkDestroyed>()
|
||||
|
||||
// Now mark network4 awaiting replacement with a low timeout, and check that if no
|
||||
// replacement arrives, it is torn down.
|
||||
agent4.destroyAndAwaitReplacement(100 /* timeoutMillis */)
|
||||
matchAllCallback.expectCallback<Lost>(network4, 1000L /* timeoutMs */)
|
||||
testCallback.expectCallback<Lost>(network4, 1000L /* timeoutMs */)
|
||||
agent4.expectCallback<OnNetworkDestroyed>()
|
||||
agent4.expectCallback<OnNetworkUnwanted>()
|
||||
|
||||
// If a network that is awaiting replacement is unregistered, it disconnects immediately,
|
||||
// before the replacement timeout fires.
|
||||
val (agent5, network5) = connectNetwork()
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(network5)
|
||||
testCallback.expectAvailableThenValidatedCallbacks(network5)
|
||||
agent5.destroyAndAwaitReplacement(5_000 /* timeoutMillis */)
|
||||
agent5.unregister()
|
||||
matchAllCallback.expectCallback<Lost>(network5, 1000L /* timeoutMs */)
|
||||
testCallback.expectCallback<Lost>(network5, 1000L /* timeoutMs */)
|
||||
agent5.expectCallback<OnNetworkDestroyed>()
|
||||
agent5.expectCallback<OnNetworkUnwanted>()
|
||||
|
||||
// If wifi is replaced within the timeout, the device does not switch to cellular.
|
||||
val (cellAgent, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
|
||||
testCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
|
||||
|
||||
val (wifiAgent, wifiNetwork) = connectNetwork(TRANSPORT_WIFI)
|
||||
testCallback.expectAvailableCallbacks(wifiNetwork, validated = true)
|
||||
testCallback.expectCapabilitiesThat(wifiNetwork) {
|
||||
it.hasCapability(NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
matchAllCallback.expectAvailableCallbacks(wifiNetwork, validated = false)
|
||||
matchAllCallback.expectCallback<Losing>(cellNetwork)
|
||||
matchAllCallback.expectCapabilitiesThat(wifiNetwork) {
|
||||
it.hasCapability(NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
|
||||
wifiAgent.destroyAndAwaitReplacement(5_000 /* timeoutMillis */)
|
||||
wifiAgent.expectCallback<OnNetworkDestroyed>()
|
||||
|
||||
// Once the network is awaiting replacement, changing LinkProperties, NetworkCapabilities or
|
||||
// score, or calling reportNetworkConnectivity, have no effect.
|
||||
val wifiSpecifier = mCM.getNetworkCapabilities(wifiNetwork)!!.networkSpecifier
|
||||
assertNotNull(wifiSpecifier)
|
||||
assertTrue(wifiSpecifier is EthernetNetworkSpecifier)
|
||||
|
||||
val wifiNc = makeTestNetworkCapabilities(wifiSpecifier.interfaceName,
|
||||
intArrayOf(TRANSPORT_WIFI))
|
||||
wifiAgent.sendNetworkCapabilities(wifiNc)
|
||||
val wifiLp = mCM.getLinkProperties(wifiNetwork)!!
|
||||
val newRoute = RouteInfo(IpPrefix("192.0.2.42/24"))
|
||||
assertFalse(wifiLp.getRoutes().contains(newRoute))
|
||||
wifiLp.addRoute(newRoute)
|
||||
wifiAgent.sendLinkProperties(wifiLp)
|
||||
mCM.reportNetworkConnectivity(wifiNetwork, false)
|
||||
// The test implicitly checks that no callbacks are sent here, because the next events seen
|
||||
// by the callbacks are for the new network connecting.
|
||||
|
||||
val (newWifiAgent, newWifiNetwork) = connectNetwork(TRANSPORT_WIFI)
|
||||
testCallback.expectAvailableCallbacks(newWifiNetwork, validated = true)
|
||||
matchAllCallback.expectAvailableThenValidatedCallbacks(newWifiNetwork)
|
||||
matchAllCallback.expectCallback<Lost>(wifiNetwork)
|
||||
wifiAgent.expectCallback<OnNetworkUnwanted>()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user