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 {
|
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, 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);
|
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 @Nullable public android.net.Network getNetwork();
|
||||||
method public void markConnected();
|
method public void markConnected();
|
||||||
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
|
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
|
||||||
|
|||||||
@@ -47,4 +47,5 @@ oneway interface INetworkAgentRegistry {
|
|||||||
void sendAddDscpPolicy(in DscpPolicy policy);
|
void sendAddDscpPolicy(in DscpPolicy policy);
|
||||||
void sendRemoveDscpPolicy(int policyId);
|
void sendRemoveDscpPolicy(int policyId);
|
||||||
void sendRemoveAllDscpPolicies();
|
void sendRemoveAllDscpPolicies();
|
||||||
|
void sendDestroyAndAwaitReplacement(int timeoutMillis);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -434,6 +434,14 @@ public abstract class NetworkAgent {
|
|||||||
*/
|
*/
|
||||||
public static final int CMD_DSCP_POLICY_STATUS = BASE + 28;
|
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) {
|
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
|
||||||
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
|
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
|
||||||
config.legacyTypeName, config.legacySubTypeName);
|
config.legacyTypeName, config.legacySubTypeName);
|
||||||
@@ -942,6 +950,45 @@ public abstract class NetworkAgent {
|
|||||||
queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMillis));
|
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.
|
* Change the legacy subtype of this network agent.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3503,6 +3503,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return false;
|
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.
|
// must be stateless - things change under us.
|
||||||
private class NetworkStateTrackerHandler extends Handler {
|
private class NetworkStateTrackerHandler extends Handler {
|
||||||
public NetworkStateTrackerHandler(Looper looper) {
|
public NetworkStateTrackerHandler(Looper looper) {
|
||||||
@@ -3519,6 +3525,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
return;
|
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) {
|
switch (msg.what) {
|
||||||
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
|
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
|
||||||
final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
|
final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
|
||||||
@@ -3620,12 +3631,60 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
break;
|
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) {
|
private boolean maybeHandleNetworkMonitorMessage(Message msg) {
|
||||||
final int netId = msg.arg2;
|
final int netId = msg.arg2;
|
||||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
|
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) {
|
switch (msg.what) {
|
||||||
default:
|
default:
|
||||||
return false;
|
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) {
|
private void handleNetworkAgentDisconnected(Message msg) {
|
||||||
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
|
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
|
||||||
disconnectAndDestroyNetwork(nai);
|
disconnectAndDestroyNetwork(nai);
|
||||||
@@ -4231,7 +4294,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void destroyNetwork(NetworkAgentInfo nai) {
|
private void destroyNetwork(NetworkAgentInfo nai) {
|
||||||
if (nai.created) {
|
if (shouldDestroyNativeNetwork(nai)) {
|
||||||
// Tell netd to clean up the configuration for this network
|
// Tell netd to clean up the configuration for this network
|
||||||
// (routing rules, DNS, etc).
|
// (routing rules, DNS, etc).
|
||||||
// This may be slow as it requires a lot of netd shelling out to ip and
|
// 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
|
// network or service a new request from an app), so network traffic isn't interrupted
|
||||||
// for an unnecessarily long time.
|
// for an unnecessarily long time.
|
||||||
destroyNativeNetwork(nai);
|
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());
|
mNetIdManager.releaseNetId(nai.network.getNetId());
|
||||||
nai.onNetworkDestroyed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) {
|
private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) {
|
||||||
@@ -4291,6 +4354,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
} catch (RemoteException | ServiceSpecificException e) {
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
loge("Exception destroying network: " + 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
|
// 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());
|
log(" accepting network in place of " + previousSatisfier.toShortString());
|
||||||
}
|
}
|
||||||
previousSatisfier.removeRequest(previousRequest.requestId);
|
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
|
// 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
|
// 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
|
// performance on the new network, since the radio can't do both at the same
|
||||||
// time while preserving good performance.
|
// 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);
|
previousSatisfier.lingerRequest(previousRequest.requestId, now);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public class Nat464Xlat {
|
|||||||
final boolean skip464xlat = (nai.netAgentConfig() != null)
|
final boolean skip464xlat = (nai.netAgentConfig() != null)
|
||||||
&& nai.netAgentConfig().skip464xlat;
|
&& nai.netAgentConfig().skip464xlat;
|
||||||
|
|
||||||
return supported && connected && isIpv6OnlyNetwork && !skip464xlat
|
return supported && connected && isIpv6OnlyNetwork && !skip464xlat && !nai.destroyed
|
||||||
&& (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
|
&& (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
|
||||||
? isCellular464XlatEnabled() : true);
|
? isCellular464XlatEnabled() : true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -732,6 +732,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
|
|||||||
mHandler.obtainMessage(NetworkAgent.EVENT_REMOVE_ALL_DSCP_POLICIES,
|
mHandler.obtainMessage(NetworkAgent.EVENT_REMOVE_ALL_DSCP_POLICIES,
|
||||||
new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
|
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.
|
* 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() {
|
public void updateScoreForNetworkAgentUpdate() {
|
||||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
|
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
|
||||||
@@ -1256,6 +1262,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
|
|||||||
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
|
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
|
||||||
+ networkInfo.toShortString() + "} "
|
+ networkInfo.toShortString() + "} "
|
||||||
+ mScore + " "
|
+ mScore + " "
|
||||||
|
+ (created ? " created" : "")
|
||||||
|
+ (destroyed ? " destroyed" : "")
|
||||||
+ (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
|
+ (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
|
||||||
+ (everValidated ? " everValidated" : "")
|
+ (everValidated ? " everValidated" : "")
|
||||||
+ (lastValidated ? " lastValidated" : "")
|
+ (lastValidated ? " lastValidated" : "")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.Manifest.permission.NETWORK_SETTINGS
|
|||||||
import android.app.Instrumentation
|
import android.app.Instrumentation
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.EthernetNetworkSpecifier
|
||||||
import android.net.INetworkAgent
|
import android.net.INetworkAgent
|
||||||
import android.net.INetworkAgentRegistry
|
import android.net.INetworkAgentRegistry
|
||||||
import android.net.InetAddresses
|
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_INTERNET
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
|
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_ROAMING
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
|
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_TEMPORARILY_NOT_METERED
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||||
|
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||||
import android.net.NetworkCapabilities.TRANSPORT_TEST
|
import android.net.NetworkCapabilities.TRANSPORT_TEST
|
||||||
|
import android.net.NetworkCapabilities.TRANSPORT_WIFI
|
||||||
import android.net.NetworkCapabilities.TRANSPORT_VPN
|
import android.net.NetworkCapabilities.TRANSPORT_VPN
|
||||||
import android.net.NetworkInfo
|
import android.net.NetworkInfo
|
||||||
import android.net.NetworkProvider
|
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.OnUnregisterQosCallback
|
||||||
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
|
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
|
||||||
import com.android.testutils.TestableNetworkCallback
|
import com.android.testutils.TestableNetworkCallback
|
||||||
|
import com.android.testutils.assertThrows
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assume.assumeFalse
|
import org.junit.Assume.assumeFalse
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -112,6 +117,8 @@ import org.mockito.Mockito.doReturn
|
|||||||
import org.mockito.Mockito.mock
|
import org.mockito.Mockito.mock
|
||||||
import org.mockito.Mockito.timeout
|
import org.mockito.Mockito.timeout
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.DatagramSocket
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
@@ -249,14 +256,10 @@ class NetworkAgentTest {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNetworkAgent(
|
private fun makeTestNetworkCapabilities(
|
||||||
context: Context = realContext,
|
|
||||||
specifier: String? = null,
|
specifier: String? = null,
|
||||||
initialNc: NetworkCapabilities? = null,
|
transports: IntArray = intArrayOf()
|
||||||
initialLp: LinkProperties? = null,
|
) = NetworkCapabilities().apply {
|
||||||
initialConfig: NetworkAgentConfig? = null
|
|
||||||
): TestableNetworkAgent {
|
|
||||||
val nc = initialNc ?: NetworkCapabilities().apply {
|
|
||||||
addTransportType(TRANSPORT_TEST)
|
addTransportType(TRANSPORT_TEST)
|
||||||
removeCapability(NET_CAPABILITY_TRUSTED)
|
removeCapability(NET_CAPABILITY_TRUSTED)
|
||||||
removeCapability(NET_CAPABILITY_INTERNET)
|
removeCapability(NET_CAPABILITY_INTERNET)
|
||||||
@@ -269,7 +272,20 @@ class NetworkAgentTest {
|
|||||||
if (null != specifier) {
|
if (null != specifier) {
|
||||||
setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(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 {
|
val lp = initialLp ?: LinkProperties().apply {
|
||||||
addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
|
addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
|
||||||
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
|
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
|
||||||
@@ -284,12 +300,14 @@ class NetworkAgentTest {
|
|||||||
context: Context = realContext,
|
context: Context = realContext,
|
||||||
specifier: String? = UUID.randomUUID().toString(),
|
specifier: String? = UUID.randomUUID().toString(),
|
||||||
initialConfig: NetworkAgentConfig? = null,
|
initialConfig: NetworkAgentConfig? = null,
|
||||||
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf()
|
expectedInitSignalStrengthThresholds: IntArray? = intArrayOf(),
|
||||||
|
transports: IntArray = intArrayOf()
|
||||||
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
|
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
|
||||||
val callback = TestableNetworkCallback()
|
val callback = TestableNetworkCallback()
|
||||||
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
|
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
|
||||||
requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
|
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)
|
agent.setTeardownDelayMillis(0)
|
||||||
// Connect the agent and verify initial status callbacks.
|
// Connect the agent and verify initial status callbacks.
|
||||||
agent.register()
|
agent.register()
|
||||||
@@ -301,6 +319,15 @@ class NetworkAgentTest {
|
|||||||
return agent to callback
|
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 {
|
private fun createNetworkAgentWithFakeCS() = createNetworkAgent().also {
|
||||||
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
|
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
|
||||||
}
|
}
|
||||||
@@ -1123,4 +1150,138 @@ class NetworkAgentTest {
|
|||||||
remoteAddresses
|
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