Merge changes Ib1cd342a,Iab23d414 am: 1c5ce5542a

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/1991622

Change-Id: I3aab26bc9351e7dd065071b11d58ef9be7224a92
This commit is contained in:
Treehugger Robot
2022-02-22 09:35:16 +00:00
committed by Automerger Merge Worker
4 changed files with 58 additions and 25 deletions

View File

@@ -21,8 +21,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkScore.KEEP_CONNECTED_NONE; import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
import static android.net.NetworkScore.POLICY_EXITING;
import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI; import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
import android.annotation.IntDef; import android.annotation.IntDef;
@@ -31,8 +29,10 @@ import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkScore; import android.net.NetworkScore;
import android.net.NetworkScore.KeepConnectedReason; import android.net.NetworkScore.KeepConnectedReason;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -98,9 +98,17 @@ public class FullScore {
/** @hide */ /** @hide */
public static final int POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD = 57; public static final int POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD = 57;
// The network agent has communicated that this network no longer functions, and the underlying
// native network has been destroyed. The network will still be reported to clients as connected
// until a timeout expires, the agent disconnects, or the network no longer satisfies requests.
// This network should lose to an identical network that has not been destroyed, but should
// otherwise be scored exactly the same.
/** @hide */
public static final int POLICY_IS_DESTROYED = 56;
// To help iterate when printing // To help iterate when printing
@VisibleForTesting @VisibleForTesting
static final int MIN_CS_MANAGED_POLICY = POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD; static final int MIN_CS_MANAGED_POLICY = POLICY_IS_DESTROYED;
@VisibleForTesting @VisibleForTesting
static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED; static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
@@ -112,21 +120,14 @@ public class FullScore {
private static final long EXTERNAL_POLICIES_MASK = private static final long EXTERNAL_POLICIES_MASK =
0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI); 0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI);
private static SparseArray<String> sMessageNames = MessageUtils.findMessageNames(
new Class[]{FullScore.class, NetworkScore.class}, new String[]{"POLICY_"});
@VisibleForTesting @VisibleForTesting
static @NonNull String policyNameOf(final int policy) { static @NonNull String policyNameOf(final int policy) {
switch (policy) { final String name = sMessageNames.get(policy);
case POLICY_IS_VALIDATED: return "IS_VALIDATED"; if (name == null) throw new IllegalArgumentException("Unknown policy: " + policy);
case POLICY_IS_VPN: return "IS_VPN"; return name.substring("POLICY_".length());
case POLICY_EVER_USER_SELECTED: return "EVER_USER_SELECTED";
case POLICY_ACCEPT_UNVALIDATED: return "ACCEPT_UNVALIDATED";
case POLICY_IS_UNMETERED: return "IS_UNMETERED";
case POLICY_YIELD_TO_BAD_WIFI: return "YIELD_TO_BAD_WIFI";
case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY";
case POLICY_EXITING: return "EXITING";
case POLICY_IS_INVINCIBLE: return "INVINCIBLE";
case POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD: return "EVER_VALIDATED";
}
throw new IllegalArgumentException("Unknown policy : " + policy);
} }
// Bitmask of all the policies applied to this score. // Bitmask of all the policies applied to this score.
@@ -149,6 +150,7 @@ public class FullScore {
* @param config the NetworkAgentConfig of the network * @param config the NetworkAgentConfig of the network
* @param everValidated whether this network has ever validated * @param everValidated whether this network has ever validated
* @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad * @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad
* @param destroyed whether this network has been destroyed pending a replacement connecting
* @return a FullScore that is appropriate to use for ranking. * @return a FullScore that is appropriate to use for ranking.
*/ */
// TODO : this shouldn't manage bad wifi avoidance instead this should be done by the // TODO : this shouldn't manage bad wifi avoidance instead this should be done by the
@@ -156,7 +158,7 @@ public class FullScore {
// connectivity for backward compatibility. // connectivity for backward compatibility.
public static FullScore fromNetworkScore(@NonNull final NetworkScore score, public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config, @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
final boolean everValidated, final boolean yieldToBadWiFi) { final boolean everValidated, final boolean yieldToBadWiFi, final boolean destroyed) {
return withPolicies(score.getLegacyInt(), score.getPolicies(), return withPolicies(score.getLegacyInt(), score.getPolicies(),
score.getKeepConnectedReason(), score.getKeepConnectedReason(),
caps.hasCapability(NET_CAPABILITY_VALIDATED), caps.hasCapability(NET_CAPABILITY_VALIDATED),
@@ -166,6 +168,7 @@ public class FullScore {
config.explicitlySelected, config.explicitlySelected,
config.acceptUnvalidated, config.acceptUnvalidated,
yieldToBadWiFi, yieldToBadWiFi,
destroyed,
false /* invincible */); // only prospective scores can be invincible false /* invincible */); // only prospective scores can be invincible
} }
@@ -174,7 +177,7 @@ public class FullScore {
* *
* NetworkOffers have score filters that are compared to the scores of actual networks * NetworkOffers have score filters that are compared to the scores of actual networks
* to see if they could possibly beat the current satisfier. Some things the agent can't * to see if they could possibly beat the current satisfier. Some things the agent can't
* know in advance ; a good example is the validation bit some networks will validate, * know in advance; a good example is the validation bit some networks will validate,
* others won't. For comparison purposes, assume the best, so all possibly beneficial * others won't. For comparison purposes, assume the best, so all possibly beneficial
* networks will be brought up. * networks will be brought up.
* *
@@ -197,12 +200,14 @@ public class FullScore {
final boolean everUserSelected = false; final boolean everUserSelected = false;
// Don't assume the user will accept unvalidated connectivity. // Don't assume the user will accept unvalidated connectivity.
final boolean acceptUnvalidated = false; final boolean acceptUnvalidated = false;
// A network can only be destroyed once it has connected.
final boolean destroyed = false;
// A prospective score is invincible if the legacy int in the filter is over the maximum // A prospective score is invincible if the legacy int in the filter is over the maximum
// score. // score.
final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX; final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE, return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated, mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
yieldToBadWiFi, invincible); yieldToBadWiFi, destroyed, invincible);
} }
/** /**
@@ -218,7 +223,8 @@ public class FullScore {
public FullScore mixInScore(@NonNull final NetworkCapabilities caps, public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
@NonNull final NetworkAgentConfig config, @NonNull final NetworkAgentConfig config,
final boolean everValidated, final boolean everValidated,
final boolean yieldToBadWifi) { final boolean yieldToBadWifi,
final boolean destroyed) {
return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason, return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason,
caps.hasCapability(NET_CAPABILITY_VALIDATED), caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN), caps.hasTransport(TRANSPORT_VPN),
@@ -227,6 +233,7 @@ public class FullScore {
config.explicitlySelected, config.explicitlySelected,
config.acceptUnvalidated, config.acceptUnvalidated,
yieldToBadWifi, yieldToBadWifi,
destroyed,
false /* invincible */); // only prospective scores can be invincible false /* invincible */); // only prospective scores can be invincible
} }
@@ -243,6 +250,7 @@ public class FullScore {
final boolean everUserSelected, final boolean everUserSelected,
final boolean acceptUnvalidated, final boolean acceptUnvalidated,
final boolean yieldToBadWiFi, final boolean yieldToBadWiFi,
final boolean destroyed,
final boolean invincible) { final boolean invincible) {
return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK) return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK)
| (isValidated ? 1L << POLICY_IS_VALIDATED : 0) | (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
@@ -252,6 +260,7 @@ public class FullScore {
| (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0) | (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0)
| (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0) | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0)
| (yieldToBadWiFi ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0) | (yieldToBadWiFi ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0)
| (destroyed ? 1L << POLICY_IS_DESTROYED : 0)
| (invincible ? 1L << POLICY_IS_INVINCIBLE : 0), | (invincible ? 1L << POLICY_IS_INVINCIBLE : 0),
keepConnectedReason); keepConnectedReason);
} }

View File

@@ -106,6 +106,12 @@ import java.util.TreeSet;
// or tunnel) but does not disconnect from the AP/cell tower, or // or tunnel) but does not disconnect from the AP/cell tower, or
// d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes. // d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
// 5. registered, created, connected, validated // 5. registered, created, connected, validated
// 6. registered, created, connected, (validated or unvalidated), destroyed
// This is an optional state where the underlying native network is destroyed but the network is
// still connected for scoring purposes, so can satisfy requests, including the default request.
// It is used when the transport layer wants to replace a network with another network (e.g.,
// when Wi-Fi has roamed to a different BSSID that is part of a different L3 network) and does
// not want the device to switch to another network until the replacement connects and validates.
// //
// The device's default network connection: // The device's default network connection:
// ---------------------------------------- // ----------------------------------------
@@ -184,6 +190,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
// shows up in API calls, is able to satisfy NetworkRequests and can become the default network. // shows up in API calls, is able to satisfy NetworkRequests and can become the default network.
// This is a sticky bit; once set it is never cleared. // This is a sticky bit; once set it is never cleared.
public boolean everConnected; public boolean everConnected;
// Whether this network has been destroyed and is being kept temporarily until it is replaced.
public boolean destroyed;
// Set to true if this Network successfully passed validation or if it did not satisfy the // Set to true if this Network successfully passed validation or if it did not satisfy the
// default NetworkRequest in which case validation will not be attempted. // default NetworkRequest in which case validation will not be attempted.
// This is a sticky bit; once set it is never cleared even if future validation attempts fail. // This is a sticky bit; once set it is never cleared even if future validation attempts fail.
@@ -746,7 +755,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
final NetworkCapabilities oldNc = networkCapabilities; final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc; networkCapabilities = nc;
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, everValidatedForYield(), mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, everValidatedForYield(),
yieldToBadWiFi()); yieldToBadWiFi(), destroyed);
final NetworkMonitorManager nm = mNetworkMonitor; final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) { if (nm != null) {
nm.notifyNetworkCapabilitiesChanged(nc); nm.notifyNetworkCapabilitiesChanged(nc);
@@ -961,7 +970,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/ */
public void setScore(final NetworkScore score) { public void setScore(final NetworkScore score) {
mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig, mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig,
everValidatedForYield(), yieldToBadWiFi()); everValidatedForYield(), yieldToBadWiFi(), destroyed);
} }
/** /**
@@ -971,7 +980,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/ */
public void updateScoreForNetworkAgentUpdate() { public void updateScoreForNetworkAgentUpdate() {
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
everValidatedForYield(), yieldToBadWiFi()); everValidatedForYield(), yieldToBadWiFi(), destroyed);
} }
private boolean everValidatedForYield() { private boolean everValidatedForYield() {

View File

@@ -28,6 +28,7 @@ import static com.android.net.module.util.CollectionUtils.filter;
import static com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED; import static com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED;
import static com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED; import static com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED;
import static com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD; import static com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD;
import static com.android.server.connectivity.FullScore.POLICY_IS_DESTROYED;
import static com.android.server.connectivity.FullScore.POLICY_IS_INVINCIBLE; import static com.android.server.connectivity.FullScore.POLICY_IS_INVINCIBLE;
import static com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED; import static com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED;
import static com.android.server.connectivity.FullScore.POLICY_IS_VPN; import static com.android.server.connectivity.FullScore.POLICY_IS_VPN;
@@ -263,6 +264,15 @@ public class NetworkRanker {
} }
} }
// If two networks are equivalent, and one has been destroyed pending replacement, keep the
// other one. This ensures that when the replacement connects, it's preferred.
partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_IS_DESTROYED),
accepted, rejected);
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) {
candidates = new ArrayList<>(accepted);
}
// At this point there are still multiple networks passing all the tests above. If any // At this point there are still multiple networks passing all the tests above. If any
// of them is the previous satisfier, keep it. // of them is the previous satisfier, keep it.
if (candidates.contains(currentSatisfier)) return currentSatisfier; if (candidates.contains(currentSatisfier)) return currentSatisfier;

View File

@@ -26,6 +26,8 @@ import androidx.test.filters.SmallTest
import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED
import com.android.server.connectivity.FullScore.POLICY_IS_DESTROYED
import com.android.server.connectivity.FullScore.POLICY_IS_UNMETERED
import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
import com.android.server.connectivity.FullScore.POLICY_IS_VPN import com.android.server.connectivity.FullScore.POLICY_IS_VPN
import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule
@@ -47,7 +49,8 @@ class FullScoreTest {
validated: Boolean = false, validated: Boolean = false,
vpn: Boolean = false, vpn: Boolean = false,
onceChosen: Boolean = false, onceChosen: Boolean = false,
acceptUnvalidated: Boolean = false acceptUnvalidated: Boolean = false,
destroyed: Boolean = false
): FullScore { ): FullScore {
val nac = NetworkAgentConfig.Builder().apply { val nac = NetworkAgentConfig.Builder().apply {
setUnvalidatedConnectivityAcceptable(acceptUnvalidated) setUnvalidatedConnectivityAcceptable(acceptUnvalidated)
@@ -57,7 +60,7 @@ class FullScoreTest {
if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN) if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}.build() }.build()
return mixInScore(nc, nac, validated, false /* yieldToBadWifi */) return mixInScore(nc, nac, validated, false /* yieldToBadWifi */, destroyed)
} }
@Test @Test
@@ -101,6 +104,7 @@ class FullScoreTest {
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1) FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1)
} }
assertEquals("IS_UNMETERED", FullScore.policyNameOf(POLICY_IS_UNMETERED))
} }
fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex -> fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex ->
@@ -118,6 +122,7 @@ class FullScoreTest {
assertTrue(ns.withPolicies(vpn = true).hasPolicy(POLICY_IS_VPN)) assertTrue(ns.withPolicies(vpn = true).hasPolicy(POLICY_IS_VPN))
assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED)) assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED))
assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED)) assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED))
assertTrue(ns.withPolicies(destroyed = true).hasPolicy(POLICY_IS_DESTROYED))
} }
@Test @Test