Merge changes from topics "enable-policy-ranking", "sendNetworkScore"

* changes:
  Adjust a test for policy scoring
  Enable policy ranking
  Add CTS to verify NetworkAgent#setLingerDuration
  Allow network providers to set the linger duration.
  Fix nascent timer never get removed
  [NS13] Remove the last usage of the legacy int
  Add documentation
  Use filter from CollectionUtils.
  Fix a bug where updates of offers won't find existing offers
  [NS12] Address comments on NS09
  [NS11] Fix yieldToBadWifi over the policy scoring
  Add doc for NetworkScore#getLegacyInt
  Expose sendNetworkScore that takes a NetworkScore
This commit is contained in:
Junyu Lai
2021-06-08 22:34:44 +00:00
committed by Gerrit Code Review
15 changed files with 348 additions and 119 deletions

View File

@@ -232,12 +232,14 @@ package android.net {
method @NonNull public android.net.Network register(); method @NonNull public android.net.Network register();
method public final void sendLinkProperties(@NonNull android.net.LinkProperties); method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
method public final void sendNetworkScore(@NonNull android.net.NetworkScore);
method public final void sendNetworkScore(@IntRange(from=0, to=99) int); method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
method public final void sendQosCallbackError(int, int); method public final void sendQosCallbackError(int, int);
method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes); method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
method public final void sendQosSessionLost(int, int, int); method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int); method public final void sendSocketKeepaliveEvent(int, int);
method @Deprecated public void setLegacySubtype(int, @NonNull String); method @Deprecated public void setLegacySubtype(int, @NonNull String);
method public void setLingerDuration(@NonNull java.time.Duration);
method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int); method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister(); method public void unregister();

View File

@@ -42,4 +42,5 @@ oneway interface INetworkAgentRegistry {
void sendQosSessionLost(int qosCallbackId, in QosSession session); void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType); void sendQosCallbackError(int qosCallbackId, int exceptionType);
void sendTeardownDelayMs(int teardownDelayMs); void sendTeardownDelayMs(int teardownDelayMs);
void sendLingerDuration(int durationMs);
} }

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.SystemApi; import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
@@ -106,6 +107,9 @@ public abstract class NetworkAgent {
private final String LOG_TAG; private final String LOG_TAG;
private static final boolean DBG = true; private static final boolean DBG = true;
private static final boolean VDBG = false; private static final boolean VDBG = false;
/** @hide */
@TestApi
public static final int MIN_LINGER_TIMER_MS = 2000;
private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>(); private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>();
private volatile long mLastBwRefreshTime = 0; private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500; private static final long BW_REFRESH_MIN_WIN_MS = 500;
@@ -391,6 +395,15 @@ public abstract class NetworkAgent {
*/ */
public static final int CMD_NETWORK_DESTROYED = BASE + 23; public static final int CMD_NETWORK_DESTROYED = BASE + 23;
/**
* Sent by the NetworkAgent to ConnectivityService to set the linger duration for this network
* agent.
* arg1 = the linger duration, represents by {@link Duration}.
*
* @hide
*/
public static final int EVENT_LINGER_DURATION_CHANGED = BASE + 24;
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);
@@ -956,7 +969,6 @@ public abstract class NetworkAgent {
* Must be called by the agent to update the score of this network. * Must be called by the agent to update the score of this network.
* *
* @param score the new score. * @param score the new score.
* @hide TODO : unhide when impl is complete
*/ */
public final void sendNetworkScore(@NonNull NetworkScore score) { public final void sendNetworkScore(@NonNull NetworkScore score) {
Objects.requireNonNull(score); Objects.requireNonNull(score);
@@ -1288,6 +1300,22 @@ public abstract class NetworkAgent {
queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType)); queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
} }
/**
* Set the linger duration for this network agent.
* @param duration the delay between the moment the network becomes unneeded and the
* moment the network is disconnected or moved into the background.
* Note that If this duration has greater than millisecond precision, then
* the internal implementation will drop any excess precision.
*/
public void setLingerDuration(@NonNull final Duration duration) {
Objects.requireNonNull(duration);
final long durationMs = duration.toMillis();
if (durationMs < MIN_LINGER_TIMER_MS || durationMs > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Duration must be within ["
+ MIN_LINGER_TIMER_MS + "," + Integer.MAX_VALUE + "]ms");
}
queueOrSendMessage(ra -> ra.sendLingerDuration((int) durationMs));
}
/** @hide */ /** @hide */
protected void log(final String s) { protected void log(final String s) {

View File

@@ -167,7 +167,15 @@ public class NetworkProvider {
ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request); ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
} }
/** @hide */ /**
* A callback for parties registering a NetworkOffer.
*
* This is used with {@link ConnectivityManager#offerNetwork}. When offering a network,
* the system will use this callback to inform the caller that a network corresponding to
* this offer is needed or unneeded.
*
* @hide
*/
@SystemApi @SystemApi
public interface NetworkOfferCallback { public interface NetworkOfferCallback {
/** /**

View File

@@ -48,7 +48,14 @@ public final class NetworkScore implements Parcelable {
}) })
public @interface KeepConnectedReason { } public @interface KeepConnectedReason { }
/**
* Do not keep this network connected if there is no outstanding request for it.
*/
public static final int KEEP_CONNECTED_NONE = 0; public static final int KEEP_CONNECTED_NONE = 0;
/**
* Keep this network connected even if there is no outstanding request for it, because it
* is being considered for handover.
*/
public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
// Agent-managed policies // Agent-managed policies
@@ -93,6 +100,10 @@ public final class NetworkScore implements Parcelable {
mKeepConnectedReason = in.readInt(); mKeepConnectedReason = in.readInt();
} }
/**
* Get the legacy int score embedded in this NetworkScore.
* @see Builder#setLegacyInt(int)
*/
public int getLegacyInt() { public int getLegacyInt() {
return mLegacyInt; return mLegacyInt;
} }
@@ -212,7 +223,9 @@ public final class NetworkScore implements Parcelable {
/** /**
* Sets the legacy int for this score. * Sets the legacy int for this score.
* *
* Do not rely on this. It will be gone by the time S is released. * This will be used for measurements and logs, but will no longer be used for ranking
* networks against each other. Callers that existed before Android S should send what
* they used to send as the int score.
* *
* @param score the legacy int * @param score the legacy int
* @return this * @return this

View File

@@ -1400,8 +1400,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
new NetworkInfo(TYPE_NONE, 0, "", ""), new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(), new LinkProperties(), new NetworkCapabilities(),
new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker, new NetworkAgentConfig(), this, null, null, 0, INVALID_UID,
mDeps); mLingerDelayMs, mQosCallbackTracker, mDeps);
} }
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -3234,6 +3234,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else { } else {
logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1); logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
} }
break;
}
case NetworkAgent.EVENT_LINGER_DURATION_CHANGED: {
nai.setLingerDuration((int) arg.second);
break;
} }
} }
} }
@@ -4038,7 +4043,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
// is important so as to not evaluate lower priority requests further in // is important so as to not evaluate lower priority requests further in
// nri.mRequests. // nri.mRequests.
final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId) final NetworkAgentInfo champion = req.equals(nri.getActiveRequest())
? nri.getSatisfier() : null;
// Note that this catches two important cases: // Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// is currently satisfying the request. This is desirable when // is currently satisfying the request. This is desirable when
@@ -4046,9 +4052,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 2. Unvalidated WiFi will not be reaped when validated cellular // 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when // is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular. // WiFi ends up validating and out scoring cellular.
|| nri.getSatisfier().getCurrentScore() return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
} }
} }
@@ -4305,7 +4309,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// network, we should respect the user's option and don't need to popup the // network, we should respect the user's option and don't need to popup the
// PARTIAL_CONNECTIVITY notification to user again. // PARTIAL_CONNECTIVITY notification to user again.
nai.networkAgentConfig.acceptPartialConnectivity = accept; nai.networkAgentConfig.acceptPartialConnectivity = accept;
nai.updateScoreForNetworkAgentConfigUpdate(); nai.updateScoreForNetworkAgentUpdate();
rematchAllNetworksAndRequests(); rematchAllNetworksAndRequests();
} }
@@ -4373,6 +4377,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
if (!nai.avoidUnvalidated) { if (!nai.avoidUnvalidated) {
nai.avoidUnvalidated = true; nai.avoidUnvalidated = true;
nai.updateScoreForNetworkAgentUpdate();
rematchAllNetworksAndRequests(); rematchAllNetworksAndRequests();
} }
} }
@@ -4480,7 +4485,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void updateAvoidBadWifi() { private void updateAvoidBadWifi() {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) { for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
nai.updateScoreForNetworkAgentConfigUpdate(); nai.updateScoreForNetworkAgentUpdate();
} }
rematchAllNetworksAndRequests(); rematchAllNetworksAndRequests();
} }
@@ -6597,7 +6602,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = new NetworkAgentInfo(na, final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps); this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs,
mQosCallbackTracker, mDeps);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says. // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc); processCapabilitiesFromAgent(nai, nc);
@@ -6697,7 +6703,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull final INetworkOfferCallback callback) { @NonNull final INetworkOfferCallback callback) {
ensureRunningOnConnectivityServiceThread(); ensureRunningOnConnectivityServiceThread();
for (final NetworkOfferInfo noi : mNetworkOffers) { for (final NetworkOfferInfo noi : mNetworkOffers) {
if (noi.offer.callback.equals(callback)) return noi; if (noi.offer.callback.asBinder().equals(callback.asBinder())) return noi;
} }
return null; return null;
} }
@@ -7255,6 +7261,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc); final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
updateUids(nai, prevNc, newNc); updateUids(nai, prevNc, newNc);
nai.updateScoreForNetworkAgentUpdate();
if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) { if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
// If the requestable capabilities haven't changed, and the score hasn't changed, then // If the requestable capabilities haven't changed, and the score hasn't changed, then
@@ -7840,7 +7847,7 @@ 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);
previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs); previousSatisfier.lingerRequest(previousRequest.requestId, now);
} else { } else {
if (VDBG || DDBG) log(" accepting network in place of null"); if (VDBG || DDBG) log(" accepting network in place of null");
} }
@@ -7850,6 +7857,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// all networks except in the case of an underlying network for a VCN. // all networks except in the case of an underlying network for a VCN.
if (newSatisfier.isNascent()) { if (newSatisfier.isNascent()) {
newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE); newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
newSatisfier.unsetInactive();
} }
// if newSatisfier is not null, then newRequest may not be null. // if newSatisfier is not null, then newRequest may not be null.
@@ -7885,7 +7893,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull final Collection<NetworkRequestInfo> networkRequests) { @NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment(); final NetworkReassignment changes = new NetworkReassignment();
// Gather the list of all relevant agents and sort them by score. // Gather the list of all relevant agents.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>(); final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) { for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
if (!nai.everConnected) { if (!nai.everConnected) {
@@ -8354,6 +8362,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// But it will be removed as soon as the network satisfies a request for the first time. // But it will be removed as soon as the network satisfies a request for the first time.
networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE, networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
SystemClock.elapsedRealtime(), mNascentDelayMs); SystemClock.elapsedRealtime(), mNascentDelayMs);
networkAgent.setInactive();
// Consider network even though it is not yet validated. // Consider network even though it is not yet validated.
rematchAllNetworksAndRequests(); rematchAllNetworksAndRequests();

View File

@@ -91,17 +91,26 @@ public class FullScore {
/** @hide */ /** @hide */
public static final int POLICY_IS_INVINCIBLE = 58; public static final int POLICY_IS_INVINCIBLE = 58;
// This network has been validated at least once since it was connected, but not explicitly
// avoided in UI.
// TODO : remove setAvoidUnvalidated and instead disconnect the network when the user
// chooses to move away from this network, and remove this flag.
/** @hide */
public static final int POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD = 57;
// To help iterate when printing // To help iterate when printing
@VisibleForTesting @VisibleForTesting
static final int MIN_CS_MANAGED_POLICY = POLICY_IS_INVINCIBLE; static final int MIN_CS_MANAGED_POLICY = POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD;
@VisibleForTesting @VisibleForTesting
static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED; static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
// Mask for policies in NetworkScore. This should have all bits managed by NetworkScore set // Mask for policies in NetworkScore. This should have all bits managed by NetworkScore set
// and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and // and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and
// from 63 down in FullScore, cut at the 32rd bit for simplicity, but change this if some day // from 63 down in FullScore, cut at the 32nd bit for simplicity, but change this if some day
// there are more than 32 bits handled on either side. // there are more than 32 bits handled on either side.
private static final int EXTERNAL_POLICIES_MASK = 0x0000FFFF; // YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService.
private static final long EXTERNAL_POLICIES_MASK =
0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI);
@VisibleForTesting @VisibleForTesting
static @NonNull String policyNameOf(final int policy) { static @NonNull String policyNameOf(final int policy) {
@@ -115,6 +124,7 @@ public class FullScore {
case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY"; case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY";
case POLICY_EXITING: return "EXITING"; case POLICY_EXITING: return "EXITING";
case POLICY_IS_INVINCIBLE: return "INVINCIBLE"; case POLICY_IS_INVINCIBLE: return "INVINCIBLE";
case POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD: return "EVER_VALIDATED";
} }
throw new IllegalArgumentException("Unknown policy : " + policy); throw new IllegalArgumentException("Unknown policy : " + policy);
} }
@@ -137,6 +147,7 @@ public class FullScore {
* @param score the score supplied by the agent * @param score the score supplied by the agent
* @param caps the NetworkCapabilities of the network * @param caps the NetworkCapabilities of the network
* @param config the NetworkAgentConfig of the network * @param config the NetworkAgentConfig of the network
* @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
* @return a FullScore that is appropriate to use for ranking. * @return a FullScore that is appropriate to use for ranking.
*/ */
@@ -145,12 +156,13 @@ 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 yieldToBadWiFi) { final boolean everValidated, final boolean yieldToBadWiFi) {
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),
caps.hasTransport(TRANSPORT_VPN), caps.hasTransport(TRANSPORT_VPN),
caps.hasCapability(NET_CAPABILITY_NOT_METERED), caps.hasCapability(NET_CAPABILITY_NOT_METERED),
everValidated,
config.explicitlySelected, config.explicitlySelected,
config.acceptUnvalidated, config.acceptUnvalidated,
yieldToBadWiFi, yieldToBadWiFi,
@@ -179,6 +191,8 @@ public class FullScore {
// Prospective scores are always unmetered, because unmetered networks are stronger // Prospective scores are always unmetered, because unmetered networks are stronger
// than metered networks, and it's not known in advance whether the network is metered. // than metered networks, and it's not known in advance whether the network is metered.
final boolean unmetered = true; final boolean unmetered = true;
// If the offer may validate, then it should be considered to have validated at some point
final boolean everValidated = mayValidate;
// The network hasn't been chosen by the user (yet, at least). // The network hasn't been chosen by the user (yet, at least).
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.
@@ -189,8 +203,8 @@ public class FullScore {
// 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, everUserSelected, acceptUnvalidated, yieldToBadWiFi, mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
invincible); yieldToBadWiFi, invincible);
} }
/** /**
@@ -204,11 +218,14 @@ public class FullScore {
// telephony factory, so that it depends on the carrier. For now this is handled by // telephony factory, so that it depends on the carrier. For now this is handled by
// connectivity for backward compatibility. // connectivity for backward compatibility.
public FullScore mixInScore(@NonNull final NetworkCapabilities caps, public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
@NonNull final NetworkAgentConfig config, final boolean yieldToBadWifi) { @NonNull final NetworkAgentConfig config,
final boolean everValidated,
final boolean yieldToBadWifi) {
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),
caps.hasCapability(NET_CAPABILITY_NOT_METERED), caps.hasCapability(NET_CAPABILITY_NOT_METERED),
everValidated,
config.explicitlySelected, config.explicitlySelected,
config.acceptUnvalidated, config.acceptUnvalidated,
yieldToBadWifi, yieldToBadWifi,
@@ -224,6 +241,7 @@ public class FullScore {
final boolean isValidated, final boolean isValidated,
final boolean isVpn, final boolean isVpn,
final boolean isUnmetered, final boolean isUnmetered,
final boolean everValidated,
final boolean everUserSelected, final boolean everUserSelected,
final boolean acceptUnvalidated, final boolean acceptUnvalidated,
final boolean yieldToBadWiFi, final boolean yieldToBadWiFi,
@@ -232,6 +250,7 @@ public class FullScore {
| (isValidated ? 1L << POLICY_IS_VALIDATED : 0) | (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
| (isVpn ? 1L << POLICY_IS_VPN : 0) | (isVpn ? 1L << POLICY_IS_VPN : 0)
| (isUnmetered ? 1L << POLICY_IS_UNMETERED : 0) | (isUnmetered ? 1L << POLICY_IS_UNMETERED : 0)
| (everValidated ? 1L << POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD : 0)
| (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)
@@ -239,6 +258,14 @@ public class FullScore {
keepConnectedReason); keepConnectedReason);
} }
/**
* Returns this score but validated.
*/
public FullScore asValidated() {
return new FullScore(mLegacyInt, mPolicies | (1L << POLICY_IS_VALIDATED),
mKeepConnectedReason);
}
/** /**
* For backward compatibility, get the legacy int. * For backward compatibility, get the legacy int.
* This will be removed before S is published. * This will be removed before S is published.

View File

@@ -59,6 +59,7 @@ import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService; import com.android.server.ConnectivityService;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@@ -281,6 +282,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/ */
public static final int ARG_AGENT_SUCCESS = 1; public static final int ARG_AGENT_SUCCESS = 1;
// How long this network should linger for.
private int mLingerDurationMs;
// All inactivity timers for this network, sorted by expiry time. A timer is added whenever // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or // a request is moved to a network with a better score, regardless of whether the network is or
// was lingering or not. An inactivity timer is also added when a network connects // was lingering or not. An inactivity timer is also added when a network connects
@@ -349,7 +353,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
@NonNull NetworkScore score, Context context, @NonNull NetworkScore score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) { int lingerDurationMs, QosCallbackTracker qosCallbackTracker,
ConnectivityService.Dependencies deps) {
Objects.requireNonNull(net); Objects.requireNonNull(net);
Objects.requireNonNull(info); Objects.requireNonNull(info);
Objects.requireNonNull(lp); Objects.requireNonNull(lp);
@@ -370,6 +375,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler = handler; mHandler = handler;
this.factorySerialNumber = factorySerialNumber; this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid; this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker; mQosCallbackTracker = qosCallbackTracker;
} }
@@ -685,6 +691,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED, mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
} }
@Override
public void sendLingerDuration(final int durationMs) {
mHandler.obtainMessage(NetworkAgent.EVENT_LINGER_DURATION_CHANGED,
new Pair<>(NetworkAgentInfo.this, durationMs)).sendToTarget();
}
} }
/** /**
@@ -707,7 +719,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
@NonNull final NetworkCapabilities nc) { @NonNull final NetworkCapabilities nc) {
final NetworkCapabilities oldNc = networkCapabilities; final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc; networkCapabilities = nc;
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi()); mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, everValidatedForYield(),
yieldToBadWiFi());
final NetworkMonitorManager nm = mNetworkMonitor; final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) { if (nm != null) {
nm.notifyNetworkCapabilitiesChanged(nc); nm.notifyNetworkCapabilitiesChanged(nc);
@@ -893,7 +906,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
// Caller must not mutate. This method is called frequently and making a defensive copy // Caller must not mutate. This method is called frequently and making a defensive copy
// would be too expensive. This is used by NetworkRanker.Scoreable, so it can be compared // would be too expensive. This is used by NetworkRanker.Scoreable, so it can be compared
// against other scoreables. // against other scoreables.
@Override public NetworkCapabilities getCaps() { @Override public NetworkCapabilities getCapsNoCopy() {
return networkCapabilities; return networkCapabilities;
} }
@@ -919,7 +932,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,
yieldToBadWiFi()); everValidatedForYield(), yieldToBadWiFi());
} }
/** /**
@@ -927,8 +940,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
* *
* Call this after updating the network agent config. * Call this after updating the network agent config.
*/ */
public void updateScoreForNetworkAgentConfigUpdate() { public void updateScoreForNetworkAgentUpdate() {
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi()); mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
everValidatedForYield(), yieldToBadWiFi());
}
private boolean everValidatedForYield() {
return everValidated && !avoidUnvalidated;
}
/**
* Returns a Scoreable identical to this NAI, but validated.
*
* This is useful to probe what scoring would be if this network validated, to know
* whether to provisionally keep a network that may or may not validate.
*
* @return a Scoreable identical to this NAI, but validated.
*/
public NetworkRanker.Scoreable getValidatedScoreable() {
return new NetworkRanker.Scoreable() {
@Override public FullScore getScore() {
return mScore.asValidated();
}
@Override public NetworkCapabilities getCapsNoCopy() {
return networkCapabilities;
}
};
} }
/** /**
@@ -948,13 +986,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
/** /**
* Sets the specified requestId to linger on this network for the specified time. Called by * Sets the specified requestId to linger on this network for the specified time. Called by
* ConnectivityService when the request is moved to another network with a higher score, or * ConnectivityService when any request is moved to another network with a higher score, or
* when a network is newly created. * when a network is newly created.
* *
* @param requestId The requestId of the request that no longer need to be served by this * @param requestId The requestId of the request that no longer need to be served by this
* network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
* {@code LingerTimer} for a newly created network. * {@code InactivityTimer} for a newly created network.
*/ */
// TODO: Consider creating a dedicated function for nascent network, e.g. start/stopNascent.
public void lingerRequest(int requestId, long now, long duration) { public void lingerRequest(int requestId, long now, long duration) {
if (mInactivityTimerForRequest.get(requestId) != null) { if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot // Cannot happen. Once a request is lingering on a particular network, we cannot
@@ -969,6 +1008,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mInactivityTimerForRequest.put(requestId, timer); mInactivityTimerForRequest.put(requestId, timer);
} }
/**
* Sets the specified requestId to linger on this network for the timeout set when
* initializing or modified by {@link #setLingerDuration(int)}. Called by
* ConnectivityService when any request is moved to another network with a higher score.
*
* @param requestId The requestId of the request that no longer need to be served by this
* network.
* @param now current system timestamp obtained by {@code SystemClock.elapsedRealtime}.
*/
public void lingerRequest(int requestId, long now) {
lingerRequest(requestId, now, mLingerDurationMs);
}
/** /**
* Cancel lingering. Called by ConnectivityService when a request is added to this network. * Cancel lingering. Called by ConnectivityService when a request is added to this network.
* Returns true if the given requestId was lingering on this network, false otherwise. * Returns true if the given requestId was lingering on this network, false otherwise.
@@ -1006,6 +1058,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
} }
if (newExpiry > 0) { if (newExpiry > 0) {
// If the newExpiry timestamp is in the past, the wakeup message will fire immediately.
mInactivityMessage = new WakeupMessage( mInactivityMessage = new WakeupMessage(
mContext, mHandler, mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */, "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
@@ -1035,8 +1088,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
} }
/** /**
* Return whether the network is just connected and about to be torn down because of not * Set the linger duration for this NAI.
* satisfying any request. * @param durationMs The new linger duration, in milliseconds.
*/
public void setLingerDuration(final int durationMs) {
final long diff = durationMs - mLingerDurationMs;
final ArrayList<InactivityTimer> newTimers = new ArrayList<>();
for (final InactivityTimer timer : mInactivityTimers) {
if (timer.requestId == NetworkRequest.REQUEST_ID_NONE) {
// Don't touch nascent timer, re-add as is.
newTimers.add(timer);
} else {
newTimers.add(new InactivityTimer(timer.requestId, timer.expiryMs + diff));
}
}
mInactivityTimers.clear();
mInactivityTimers.addAll(newTimers);
updateInactivityTimer();
mLingerDurationMs = durationMs;
}
/**
* Return whether the network satisfies no request, but is still being kept up
* because it has just connected less than
* {@code ConnectivityService#DEFAULT_NASCENT_DELAY_MS}ms ago and is thus still considered
* nascent. Note that nascent mechanism uses inactivity timer which isn't
* associated with a request. Thus, use {@link NetworkRequest#REQUEST_ID_NONE} to identify it.
*
*/ */
public boolean isNascent() { public boolean isNascent() {
return mInactive && mInactivityTimers.size() == 1 return mInactive && mInactivityTimers.size() == 1

View File

@@ -76,7 +76,7 @@ public class NetworkOffer implements NetworkRanker.Scoreable {
/** /**
* Get the capabilities filter of this offer * Get the capabilities filter of this offer
*/ */
@Override @NonNull public NetworkCapabilities getCaps() { @Override @NonNull public NetworkCapabilities getCapsNoCopy() {
return caps; return caps;
} }
@@ -133,7 +133,7 @@ public class NetworkOffer implements NetworkRanker.Scoreable {
* @param previousOffer the previous offer * @param previousOffer the previous offer
*/ */
public void migrateFrom(@NonNull final NetworkOffer previousOffer) { public void migrateFrom(@NonNull final NetworkOffer previousOffer) {
if (!callback.equals(previousOffer.callback)) { if (!callback.asBinder().equals(previousOffer.callback.asBinder())) {
throw new IllegalArgumentException("Can only migrate from a previous version of" throw new IllegalArgumentException("Can only migrate from a previous version of"
+ " the same offer"); + " the same offer");
} }

View File

@@ -24,8 +24,10 @@ import static android.net.NetworkScore.POLICY_EXITING;
import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY; 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 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_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;
@@ -58,25 +60,13 @@ public class NetworkRanker {
/** Get score of this scoreable */ /** Get score of this scoreable */
FullScore getScore(); FullScore getScore();
/** Get capabilities of this scoreable */ /** Get capabilities of this scoreable */
NetworkCapabilities getCaps(); NetworkCapabilities getCapsNoCopy();
} }
private static final boolean USE_POLICY_RANKING = false; private static final boolean USE_POLICY_RANKING = true;
public NetworkRanker() { } public NetworkRanker() { }
// TODO : move to module utils CollectionUtils.
@NonNull private static <T> ArrayList<T> filter(@NonNull final Collection<T> source,
@NonNull final Predicate<T> test) {
final ArrayList<T> matches = new ArrayList<>();
for (final T e : source) {
if (test.test(e)) {
matches.add(e);
}
}
return matches;
}
/** /**
* Find the best network satisfying this request among the list of passed networks. * Find the best network satisfying this request among the list of passed networks.
*/ */
@@ -158,11 +148,12 @@ public class NetworkRanker {
if (accepted.size() == 1) return accepted.get(0); if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted); if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
// Yield to bad wifi policy : if any wifi has ever been validated, keep only networks // Yield to bad wifi policy : if any wifi has ever been validated (even if it's now
// that don't yield to such a wifi network. // unvalidated), and unless it's been explicitly avoided when bad in UI, then keep only
// networks that don't yield to such a wifi network.
final boolean anyWiFiEverValidated = CollectionUtils.any(candidates, final boolean anyWiFiEverValidated = CollectionUtils.any(candidates,
nai -> nai.getScore().hasPolicy(POLICY_EVER_USER_SELECTED) nai -> nai.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
&& nai.getCaps().hasTransport(TRANSPORT_WIFI)); && nai.getCapsNoCopy().hasTransport(TRANSPORT_WIFI));
if (anyWiFiEverValidated) { if (anyWiFiEverValidated) {
partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI), partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
accepted, rejected); accepted, rejected);
@@ -206,18 +197,18 @@ public class NetworkRanker {
for (final Scoreable defaultSubNai : accepted) { for (final Scoreable defaultSubNai : accepted) {
// Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports // Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports
// as a network that has it. // as a network that has it.
final int[] transports = defaultSubNai.getCaps().getTransportTypes(); final int[] transports = defaultSubNai.getCapsNoCopy().getTransportTypes();
candidates.removeIf(nai -> !nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY) candidates.removeIf(nai -> !nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY)
&& Arrays.equals(transports, nai.getCaps().getTransportTypes())); && Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
} }
if (1 == candidates.size()) return candidates.get(0); if (1 == candidates.size()) return candidates.get(0);
// It's guaranteed candidates.size() > 0 because there is at least one with DEFAULT_SUB // It's guaranteed candidates.size() > 0 because there is at least one with the
// policy and only those without it were removed. // TRANSPORT_PRIMARY policy and only those without it were removed.
// If some of the networks have a better transport than others, keep only the ones with // If some of the networks have a better transport than others, keep only the ones with
// the best transports. // the best transports.
for (final int transport : PREFERRED_TRANSPORTS_ORDER) { for (final int transport : PREFERRED_TRANSPORTS_ORDER) {
partitionInto(candidates, nai -> nai.getCaps().hasTransport(transport), partitionInto(candidates, nai -> nai.getCapsNoCopy().hasTransport(transport),
accepted, rejected); accepted, rejected);
if (accepted.size() == 1) return accepted.get(0); if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) { if (accepted.size() > 0 && rejected.size() > 0) {
@@ -243,16 +234,17 @@ public class NetworkRanker {
NetworkAgentInfo bestNetwork = null; NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE; int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : nais) { for (final NetworkAgentInfo nai : nais) {
if (nai.getCurrentScore() > bestScore) { final int naiScore = nai.getCurrentScore();
if (naiScore > bestScore) {
bestNetwork = nai; bestNetwork = nai;
bestScore = nai.getCurrentScore(); bestScore = naiScore;
} }
} }
return bestNetwork; return bestNetwork;
} }
/** /**
* Returns whether an offer has a chance to beat a champion network for a request. * Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
* *
* Offers are sent by network providers when they think they might be able to make a network * Offers are sent by network providers when they think they might be able to make a network
* with the characteristics contained in the offer. If the offer has no chance to beat * with the characteristics contained in the offer. If the offer has no chance to beat
@@ -266,15 +258,15 @@ public class NetworkRanker {
* *
* @param request The request to evaluate against. * @param request The request to evaluate against.
* @param champion The currently best network for this request. * @param champion The currently best network for this request.
* @param offer The offer. * @param contestant The offer.
* @return Whether the offer stands a chance to beat the champion. * @return Whether the offer stands a chance to beat the champion.
*/ */
public boolean mightBeat(@NonNull final NetworkRequest request, public boolean mightBeat(@NonNull final NetworkRequest request,
@Nullable final NetworkAgentInfo champion, @Nullable final NetworkAgentInfo champion,
@NonNull final NetworkOffer offer) { @NonNull final Scoreable contestant) {
// If this network can't even satisfy the request then it can't beat anything, not // If this network can't even satisfy the request then it can't beat anything, not
// even an absence of network. It can't satisfy it anyway. // even an absence of network. It can't satisfy it anyway.
if (!request.canBeSatisfiedBy(offer.caps)) return false; if (!request.canBeSatisfiedBy(contestant.getCapsNoCopy())) return false;
// If there is no satisfying network, then this network can beat, because some network // If there is no satisfying network, then this network can beat, because some network
// is always better than no network. // is always better than no network.
if (null == champion) return true; if (null == champion) return true;
@@ -283,25 +275,24 @@ public class NetworkRanker {
// Otherwise rank them. // Otherwise rank them.
final ArrayList<Scoreable> candidates = new ArrayList<>(); final ArrayList<Scoreable> candidates = new ArrayList<>();
candidates.add(champion); candidates.add(champion);
candidates.add(offer); candidates.add(contestant);
return offer == getBestNetworkByPolicy(candidates, champion); return contestant == getBestNetworkByPolicy(candidates, champion);
} else { } else {
return mightBeatByLegacyInt(request, champion.getScore(), offer); return mightBeatByLegacyInt(champion.getScore(), contestant);
} }
} }
/** /**
* Returns whether an offer might beat a champion according to the legacy int. * Returns whether a contestant might beat a champion according to the legacy int.
*/ */
public boolean mightBeatByLegacyInt(@NonNull final NetworkRequest request, private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
@Nullable final FullScore championScore, @NonNull final Scoreable contestant) {
@NonNull final NetworkOffer offer) {
final int offerIntScore; final int offerIntScore;
if (offer.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
// If the offer might have Internet access, then it might validate. // If the offer might have Internet access, then it might validate.
offerIntScore = offer.score.getLegacyIntAsValidated(); offerIntScore = contestant.getScore().getLegacyIntAsValidated();
} else { } else {
offerIntScore = offer.score.getLegacyInt(); offerIntScore = contestant.getScore().getLegacyInt();
} }
return championScore.getLegacyInt() < offerIntScore; return championScore.getLegacyInt() < offerIntScore;
} }

View File

@@ -68,6 +68,7 @@ import android.os.Build
import android.os.HandlerThread import android.os.HandlerThread
import android.os.Looper import android.os.Looper
import android.os.Message import android.os.Message
import android.os.SystemClock
import android.util.DebugUtils.valueToString import android.util.DebugUtils.valueToString
import androidx.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import com.android.modules.utils.build.SdkLevel import com.android.modules.utils.build.SdkLevel
@@ -76,6 +77,7 @@ import com.android.testutils.CompatUtil
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.TestableNetworkCallback import com.android.testutils.TestableNetworkCallback
import org.junit.After import org.junit.After
@@ -114,6 +116,7 @@ private const val NO_CALLBACK_TIMEOUT = 200L
// requests filed by the test and should never match normal internet requests. 70 is the default // requests filed by the test and should never match normal internet requests. 70 is the default
// score of Ethernet networks, it's as good a value as any other. // score of Ethernet networks, it's as good a value as any other.
private const val TEST_NETWORK_SCORE = 70 private const val TEST_NETWORK_SCORE = 70
private const val WORSE_NETWORK_SCORE = 65
private const val BETTER_NETWORK_SCORE = 75 private const val BETTER_NETWORK_SCORE = 75
private const val FAKE_NET_ID = 1098 private const val FAKE_NET_ID = 1098
private val instrumentation: Instrumentation private val instrumentation: Instrumentation
@@ -543,16 +546,23 @@ class NetworkAgentTest {
// Connect the first Network // Connect the first Network
createConnectedNetworkAgent(name = name1).let { (agent1, _) -> createConnectedNetworkAgent(name = name1).let { (agent1, _) ->
callback.expectAvailableThenValidatedCallbacks(agent1.network) callback.expectAvailableThenValidatedCallbacks(agent1.network)
// Upgrade agent1 to a better score so that there is no ambiguity when // If using the int ranking, agent1 must be upgraded to a better score so that there is
// agent2 connects that agent1 is still better // no ambiguity when agent2 connects that agent1 is still better. If using policy
agent1.sendNetworkScore(BETTER_NETWORK_SCORE - 1) // ranking, this is not necessary.
agent1.sendNetworkScore(NetworkScore.Builder().setLegacyInt(BETTER_NETWORK_SCORE)
.build())
// Connect the second agent // Connect the second agent
createConnectedNetworkAgent(name = name2).let { (agent2, _) -> createConnectedNetworkAgent(name = name2).let { (agent2, _) ->
agent2.markConnected() agent2.markConnected()
// The callback should not see anything yet // The callback should not see anything yet. With int ranking, agent1 was upgraded
// to a stronger score beforehand. With policy ranking, agent1 is preferred by
// virtue of already satisfying the request.
callback.assertNoCallback(NO_CALLBACK_TIMEOUT) callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
// Now update the score and expect the callback now prefers agent2 // Now downgrade the score and expect the callback now prefers agent2
agent2.sendNetworkScore(BETTER_NETWORK_SCORE) agent1.sendNetworkScore(NetworkScore.Builder()
.setLegacyInt(WORSE_NETWORK_SCORE)
.setExiting(true)
.build())
callback.expectCallback<Available>(agent2.network) callback.expectCallback<Available>(agent2.network)
} }
} }
@@ -768,4 +778,71 @@ class NetworkAgentTest {
// tearDown() will unregister the requests and agents // tearDown() will unregister the requests and agents
} }
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
fun testSetLingerDuration() {
// This test will create two networks and check that the one with the stronger
// score wins out for a request that matches them both. And the weaker agent will
// be disconnected after customized linger duration.
// Connect the first Network
val name1 = UUID.randomUUID().toString()
val name2 = UUID.randomUUID().toString()
val (agent1, callback) = createConnectedNetworkAgent(name = name1)
callback.expectAvailableThenValidatedCallbacks(agent1.network!!)
// Downgrade agent1 to a worse score so that there is no ambiguity when
// agent2 connects.
agent1.sendNetworkScore(NetworkScore.Builder().setLegacyInt(WORSE_NETWORK_SCORE)
.setExiting(true).build())
// Verify invalid linger duration cannot be set.
assertFailsWith<IllegalArgumentException> {
agent1.setLingerDuration(Duration.ofMillis(-1))
}
assertFailsWith<IllegalArgumentException> { agent1.setLingerDuration(Duration.ZERO) }
assertFailsWith<IllegalArgumentException> {
agent1.setLingerDuration(Duration.ofMillis(Integer.MIN_VALUE.toLong()))
}
assertFailsWith<IllegalArgumentException> {
agent1.setLingerDuration(Duration.ofMillis(Integer.MAX_VALUE.toLong() + 1))
}
assertFailsWith<IllegalArgumentException> {
agent1.setLingerDuration(Duration.ofMillis(
NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - 1))
}
// Verify valid linger timer can be set, but it should not take effect since the network
// is still needed.
agent1.setLingerDuration(Duration.ofMillis(Integer.MAX_VALUE.toLong()))
callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
// Set to the value we want to verify the functionality.
agent1.setLingerDuration(Duration.ofMillis(NetworkAgent.MIN_LINGER_TIMER_MS.toLong()))
// Make a listener which can observe agent1 lost later.
val callbackWeaker = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
registerNetworkCallback(NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(name1))
.build(), callbackWeaker)
callbackWeaker.expectAvailableCallbacks(agent1.network!!)
// Connect the second agent with a score better than agent1. Verify the callback for
// agent1 sees the linger expiry while the callback for both sees the winner.
// Record linger start timestamp prior to send score to prevent possible race, the actual
// timestamp should be slightly late than this since the service handles update
// network score asynchronously.
val lingerStart = SystemClock.elapsedRealtime()
val agent2 = createNetworkAgent(name = name2)
agent2.register()
agent2.markConnected()
callback.expectAvailableCallbacks(agent2.network!!)
callbackWeaker.expectCallback<Losing>(agent1.network!!)
val expectedRemainingLingerDuration = lingerStart +
NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - SystemClock.elapsedRealtime()
// If the available callback is too late. The remaining duration will be reduced.
assertTrue(expectedRemainingLingerDuration > 0,
"expected remaining linger duration is $expectedRemainingLingerDuration")
callbackWeaker.assertNoCallback(expectedRemainingLingerDuration)
callbackWeaker.expectCallback<Lost>(agent1.network!!)
}
} }

View File

@@ -2683,25 +2683,6 @@ public class ConnectivityServiceTest {
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
// Cell is lingered because it would not satisfy any request, even if it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated. // it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3017,11 +2998,11 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// BUG: the network will no longer linger, even though it's validated and outscored.
// TODO: fix this.
mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true); mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback(); callback.assertNoCallback();
@@ -4580,9 +4561,8 @@ public class ConnectivityServiceTest {
expectNoRequestChanged(testFactory); expectNoRequestChanged(testFactory);
testFactory.assertRequestCountEquals(0); testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested()); assertFalse(testFactory.getMyStartRequested());
// ... and cell data to be torn down after nascent network timeout. // ... and cell data to be torn down immediately since it is no longer nascent.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
waitForIdle(); waitForIdle();
assertLength(1, mCm.getAllNetworks()); assertLength(1, mCm.getAllNetworks());
} finally { } finally {
@@ -9846,7 +9826,8 @@ public class ConnectivityServiceTest {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(), return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(), nc, new NetworkScore.Builder().setLegacyInt(0).build(),
mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies()); INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker,
new ConnectivityService.Dependencies());
} }
@Test @Test
@@ -11887,6 +11868,11 @@ public class ConnectivityServiceTest {
internetFactory.expectRequestRemove(); internetFactory.expectRequestRemove();
internetFactory.assertRequestCountEquals(0); internetFactory.assertRequestCountEquals(0);
// Create a request that holds the upcoming wifi network.
final TestNetworkCallback wifiCallback = new TestNetworkCallback();
mCm.requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
wifiCallback);
// Now WiFi connects and it's unmetered, but it's weaker than cell. // Now WiFi connects and it's unmetered, but it's weaker than cell.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -11895,7 +11881,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
// The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so // The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so
// the oemPaidFactory can't beat this no matter how high its score. // the oemPaidFactory can't beat wifi no matter how high its score.
oemPaidFactory.expectRequestRemove(); oemPaidFactory.expectRequestRemove();
expectNoRequestChanged(internetFactory); expectNoRequestChanged(internetFactory);
@@ -11906,6 +11892,7 @@ public class ConnectivityServiceTest {
// unmetered network, so the oemPaidNetworkFactory still can't beat this. // unmetered network, so the oemPaidNetworkFactory still can't beat this.
expectNoRequestChanged(oemPaidFactory); expectNoRequestChanged(oemPaidFactory);
internetFactory.expectRequestAdd(); internetFactory.expectRequestAdd();
mCm.unregisterNetworkCallback(wifiCallback);
} }
/** /**

View File

@@ -56,7 +56,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, false /* avoidBadWifi */) return mixInScore(nc, nac, validated, false /* yieldToBadWifi */)
} }
@Test @Test

View File

@@ -71,6 +71,8 @@ public class LingerMonitorTest {
static final int LOW_DAILY_LIMIT = 2; static final int LOW_DAILY_LIMIT = 2;
static final int HIGH_DAILY_LIMIT = 1000; static final int HIGH_DAILY_LIMIT = 1000;
private static final int TEST_LINGER_DELAY_MS = 400;
LingerMonitor mMonitor; LingerMonitor mMonitor;
@Mock ConnectivityService mConnService; @Mock ConnectivityService mConnService;
@@ -366,7 +368,7 @@ public class LingerMonitorTest {
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd,
mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS,
mQosCallbackTracker, new ConnectivityService.Dependencies()); mQosCallbackTracker, new ConnectivityService.Dependencies());
nai.everValidated = true; nai.everValidated = true;
return nai; return nai;

View File

@@ -16,7 +16,9 @@
package com.android.server.connectivity package com.android.server.connectivity
import android.net.NetworkCapabilities
import android.net.NetworkRequest import android.net.NetworkRequest
import android.net.NetworkScore.KEEP_CONNECTED_NONE
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import org.junit.Test import org.junit.Test
@@ -32,9 +34,13 @@ import kotlin.test.assertNull
class NetworkRankerTest { class NetworkRankerTest {
private val ranker = NetworkRanker() private val ranker = NetworkRanker()
private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also { private fun makeNai(satisfy: Boolean, legacyScore: Int) =
mock(NetworkAgentInfo::class.java).also {
doReturn(satisfy).`when`(it).satisfies(any()) doReturn(satisfy).`when`(it).satisfies(any())
doReturn(score).`when`(it).currentScore val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE)
doReturn(fs).`when`(it).getScore()
val nc = NetworkCapabilities.Builder().build()
doReturn(nc).`when`(it).getCapsNoCopy()
} }
@Test @Test