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 public final void sendLinkProperties(@NonNull android.net.LinkProperties);
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 sendQosCallbackError(int, int);
method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int);
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 final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();

View File

@@ -42,4 +42,5 @@ oneway interface INetworkAgentRegistry {
void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType);
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.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -106,6 +107,9 @@ public abstract class NetworkAgent {
private final String LOG_TAG;
private static final boolean DBG = true;
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 volatile long mLastBwRefreshTime = 0;
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;
/**
* 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) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
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.
*
* @param score the new score.
* @hide TODO : unhide when impl is complete
*/
public final void sendNetworkScore(@NonNull NetworkScore score) {
Objects.requireNonNull(score);
@@ -1288,6 +1300,22 @@ public abstract class NetworkAgent {
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 */
protected void log(final String s) {

View File

@@ -167,7 +167,15 @@ public class NetworkProvider {
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
public interface NetworkOfferCallback {
/**

View File

@@ -48,7 +48,14 @@ public final class NetworkScore implements Parcelable {
})
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;
/**
* 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;
// Agent-managed policies
@@ -93,6 +100,10 @@ public final class NetworkScore implements Parcelable {
mKeepConnectedReason = in.readInt();
}
/**
* Get the legacy int score embedded in this NetworkScore.
* @see Builder#setLegacyInt(int)
*/
public int getLegacyInt() {
return mLegacyInt;
}
@@ -212,7 +223,9 @@ public final class NetworkScore implements Parcelable {
/**
* 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
* @return this

View File

@@ -1400,8 +1400,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(),
new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
mDeps);
new NetworkAgentConfig(), this, null, null, 0, INVALID_UID,
mLingerDelayMs, mQosCallbackTracker, mDeps);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -3234,6 +3234,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
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
// is important so as to not evaluate lower priority requests further in
// 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:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// 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
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
|| nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
}
}
@@ -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
// PARTIAL_CONNECTIVITY notification to user again.
nai.networkAgentConfig.acceptPartialConnectivity = accept;
nai.updateScoreForNetworkAgentConfigUpdate();
nai.updateScoreForNetworkAgentUpdate();
rematchAllNetworksAndRequests();
}
@@ -4373,6 +4377,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (!nai.avoidUnvalidated) {
nai.avoidUnvalidated = true;
nai.updateScoreForNetworkAgentUpdate();
rematchAllNetworksAndRequests();
}
}
@@ -4480,7 +4485,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void updateAvoidBadWifi() {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
nai.updateScoreForNetworkAgentConfigUpdate();
nai.updateScoreForNetworkAgentUpdate();
}
rematchAllNetworksAndRequests();
}
@@ -6597,7 +6602,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
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.
processCapabilitiesFromAgent(nai, nc);
@@ -6697,7 +6703,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull final INetworkOfferCallback callback) {
ensureRunningOnConnectivityServiceThread();
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;
}
@@ -7255,6 +7261,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
updateUids(nai, prevNc, newNc);
nai.updateScoreForNetworkAgentUpdate();
if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
// 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());
}
previousSatisfier.removeRequest(previousRequest.requestId);
previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
previousSatisfier.lingerRequest(previousRequest.requestId, now);
} else {
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.
if (newSatisfier.isNascent()) {
newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
newSatisfier.unsetInactive();
}
// 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) {
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<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
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.
networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
SystemClock.elapsedRealtime(), mNascentDelayMs);
networkAgent.setInactive();
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();

View File

@@ -91,17 +91,26 @@ public class FullScore {
/** @hide */
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
@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
static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
// 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
// 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.
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
static @NonNull String policyNameOf(final int policy) {
@@ -115,6 +124,7 @@ public class FullScore {
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);
}
@@ -137,6 +147,7 @@ public class FullScore {
* @param score the score supplied by the agent
* @param caps the NetworkCapabilities 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
* @return a FullScore that is appropriate to use for ranking.
*/
@@ -145,12 +156,13 @@ public class FullScore {
// connectivity for backward compatibility.
public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
final boolean yieldToBadWiFi) {
final boolean everValidated, final boolean yieldToBadWiFi) {
return withPolicies(score.getLegacyInt(), score.getPolicies(),
score.getKeepConnectedReason(),
caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
caps.hasCapability(NET_CAPABILITY_NOT_METERED),
everValidated,
config.explicitlySelected,
config.acceptUnvalidated,
yieldToBadWiFi,
@@ -179,6 +191,8 @@ public class FullScore {
// 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.
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).
final boolean everUserSelected = false;
// Don't assume the user will accept unvalidated connectivity.
@@ -189,8 +203,8 @@ public class FullScore {
// score.
final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
mayValidate, vpn, unmetered, everUserSelected, acceptUnvalidated, yieldToBadWiFi,
invincible);
mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
yieldToBadWiFi, invincible);
}
/**
@@ -204,11 +218,14 @@ public class FullScore {
// telephony factory, so that it depends on the carrier. For now this is handled by
// connectivity for backward compatibility.
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,
caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
caps.hasCapability(NET_CAPABILITY_NOT_METERED),
everValidated,
config.explicitlySelected,
config.acceptUnvalidated,
yieldToBadWifi,
@@ -224,6 +241,7 @@ public class FullScore {
final boolean isValidated,
final boolean isVpn,
final boolean isUnmetered,
final boolean everValidated,
final boolean everUserSelected,
final boolean acceptUnvalidated,
final boolean yieldToBadWiFi,
@@ -232,6 +250,7 @@ public class FullScore {
| (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
| (isVpn ? 1L << POLICY_IS_VPN : 0)
| (isUnmetered ? 1L << POLICY_IS_UNMETERED : 0)
| (everValidated ? 1L << POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD : 0)
| (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0)
| (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0)
| (yieldToBadWiFi ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0)
@@ -239,6 +258,14 @@ public class FullScore {
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.
* 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 java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
@@ -281,6 +282,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/
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
// 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
@@ -349,7 +353,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
@NonNull NetworkScore score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
int lingerDurationMs, QosCallbackTracker qosCallbackTracker,
ConnectivityService.Dependencies deps) {
Objects.requireNonNull(net);
Objects.requireNonNull(info);
Objects.requireNonNull(lp);
@@ -370,6 +375,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler = handler;
this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker;
}
@@ -685,6 +691,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
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) {
final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc;
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, everValidatedForYield(),
yieldToBadWiFi());
final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) {
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
// would be too expensive. This is used by NetworkRanker.Scoreable, so it can be compared
// against other scoreables.
@Override public NetworkCapabilities getCaps() {
@Override public NetworkCapabilities getCapsNoCopy() {
return networkCapabilities;
}
@@ -919,7 +932,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
*/
public void setScore(final NetworkScore score) {
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.
*/
public void updateScoreForNetworkAgentConfigUpdate() {
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
public void updateScoreForNetworkAgentUpdate() {
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
* 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.
*
* @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
* {@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) {
if (mInactivityTimerForRequest.get(requestId) != null) {
// 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);
}
/**
* 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.
* 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 the newExpiry timestamp is in the past, the wakeup message will fire immediately.
mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"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
* satisfying any request.
* Set the linger duration for this NAI.
* @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() {
return mInactive && mInactivityTimers.size() == 1

View File

@@ -76,7 +76,7 @@ public class NetworkOffer implements NetworkRanker.Scoreable {
/**
* Get the capabilities filter of this offer
*/
@Override @NonNull public NetworkCapabilities getCaps() {
@Override @NonNull public NetworkCapabilities getCapsNoCopy() {
return caps;
}
@@ -133,7 +133,7 @@ public class NetworkOffer implements NetworkRanker.Scoreable {
* @param previousOffer the previous offer
*/
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"
+ " 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_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_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_VALIDATED;
import static com.android.server.connectivity.FullScore.POLICY_IS_VPN;
@@ -58,25 +60,13 @@ public class NetworkRanker {
/** Get score of this scoreable */
FullScore getScore();
/** 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() { }
// 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.
*/
@@ -158,11 +148,12 @@ public class NetworkRanker {
if (accepted.size() == 1) return accepted.get(0);
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
// that don't yield to such a wifi network.
// Yield to bad wifi policy : if any wifi has ever been validated (even if it's now
// 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,
nai -> nai.getScore().hasPolicy(POLICY_EVER_USER_SELECTED)
&& nai.getCaps().hasTransport(TRANSPORT_WIFI));
nai -> nai.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
&& nai.getCapsNoCopy().hasTransport(TRANSPORT_WIFI));
if (anyWiFiEverValidated) {
partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
accepted, rejected);
@@ -206,18 +197,18 @@ public class NetworkRanker {
for (final Scoreable defaultSubNai : accepted) {
// Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports
// 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)
&& Arrays.equals(transports, nai.getCaps().getTransportTypes()));
&& Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
}
if (1 == candidates.size()) return candidates.get(0);
// It's guaranteed candidates.size() > 0 because there is at least one with DEFAULT_SUB
// policy and only those without it were removed.
// It's guaranteed candidates.size() > 0 because there is at least one with the
// 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
// the best transports.
for (final int transport : PREFERRED_TRANSPORTS_ORDER) {
partitionInto(candidates, nai -> nai.getCaps().hasTransport(transport),
partitionInto(candidates, nai -> nai.getCapsNoCopy().hasTransport(transport),
accepted, rejected);
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) {
@@ -243,16 +234,17 @@ public class NetworkRanker {
NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : nais) {
if (nai.getCurrentScore() > bestScore) {
final int naiScore = nai.getCurrentScore();
if (naiScore > bestScore) {
bestNetwork = nai;
bestScore = nai.getCurrentScore();
bestScore = naiScore;
}
}
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
* 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 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.
*/
public boolean mightBeat(@NonNull final NetworkRequest request,
@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
// 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
// is always better than no network.
if (null == champion) return true;
@@ -283,25 +275,24 @@ public class NetworkRanker {
// Otherwise rank them.
final ArrayList<Scoreable> candidates = new ArrayList<>();
candidates.add(champion);
candidates.add(offer);
return offer == getBestNetworkByPolicy(candidates, champion);
candidates.add(contestant);
return contestant == getBestNetworkByPolicy(candidates, champion);
} 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,
@Nullable final FullScore championScore,
@NonNull final NetworkOffer offer) {
private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
@NonNull final Scoreable contestant) {
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.
offerIntScore = offer.score.getLegacyIntAsValidated();
offerIntScore = contestant.getScore().getLegacyIntAsValidated();
} else {
offerIntScore = offer.score.getLegacyInt();
offerIntScore = contestant.getScore().getLegacyInt();
}
return championScore.getLegacyInt() < offerIntScore;
}

View File

@@ -68,6 +68,7 @@ import android.os.Build
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.os.SystemClock
import android.util.DebugUtils.valueToString
import androidx.test.InstrumentationRegistry
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.DevSdkIgnoreRunner
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.TestableNetworkCallback
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
// score of Ethernet networks, it's as good a value as any other.
private const val TEST_NETWORK_SCORE = 70
private const val WORSE_NETWORK_SCORE = 65
private const val BETTER_NETWORK_SCORE = 75
private const val FAKE_NET_ID = 1098
private val instrumentation: Instrumentation
@@ -543,16 +546,23 @@ class NetworkAgentTest {
// Connect the first Network
createConnectedNetworkAgent(name = name1).let { (agent1, _) ->
callback.expectAvailableThenValidatedCallbacks(agent1.network)
// Upgrade agent1 to a better score so that there is no ambiguity when
// agent2 connects that agent1 is still better
agent1.sendNetworkScore(BETTER_NETWORK_SCORE - 1)
// If using the int ranking, agent1 must be upgraded to a better score so that there is
// no ambiguity when agent2 connects that agent1 is still better. If using policy
// ranking, this is not necessary.
agent1.sendNetworkScore(NetworkScore.Builder().setLegacyInt(BETTER_NETWORK_SCORE)
.build())
// Connect the second agent
createConnectedNetworkAgent(name = name2).let { (agent2, _) ->
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)
// Now update the score and expect the callback now prefers agent2
agent2.sendNetworkScore(BETTER_NETWORK_SCORE)
// Now downgrade the score and expect the callback now prefers agent2
agent1.sendNetworkScore(NetworkScore.Builder()
.setLegacyInt(WORSE_NETWORK_SCORE)
.setExiting(true)
.build())
callback.expectCallback<Available>(agent2.network)
}
}
@@ -768,4 +778,71 @@ class NetworkAgentTest {
// 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(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
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3017,11 +2998,11 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
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.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
@@ -4580,9 +4561,8 @@ public class ConnectivityServiceTest {
expectNoRequestChanged(testFactory);
testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
// ... and cell data to be torn down after nascent network timeout.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
// ... and cell data to be torn down immediately since it is no longer nascent.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertLength(1, mCm.getAllNetworks());
} finally {
@@ -9846,7 +9826,8 @@ public class ConnectivityServiceTest {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
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
@@ -11887,6 +11868,11 @@ public class ConnectivityServiceTest {
internetFactory.expectRequestRemove();
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.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -11895,7 +11881,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
// 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();
expectNoRequestChanged(internetFactory);
@@ -11906,6 +11892,7 @@ public class ConnectivityServiceTest {
// unmetered network, so the oemPaidNetworkFactory still can't beat this.
expectNoRequestChanged(oemPaidFactory);
internetFactory.expectRequestAdd();
mCm.unregisterNetworkCallback(wifiCallback);
}
/**

View File

@@ -56,7 +56,7 @@ class FullScoreTest {
if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}.build()
return mixInScore(nc, nac, false /* avoidBadWifi */)
return mixInScore(nc, nac, validated, false /* yieldToBadWifi */)
}
@Test

View File

@@ -71,6 +71,8 @@ public class LingerMonitorTest {
static final int LOW_DAILY_LIMIT = 2;
static final int HIGH_DAILY_LIMIT = 1000;
private static final int TEST_LINGER_DELAY_MS = 400;
LingerMonitor mMonitor;
@Mock ConnectivityService mConnService;
@@ -366,7 +368,7 @@ public class LingerMonitorTest {
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
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());
nai.everValidated = true;
return nai;

View File

@@ -16,7 +16,9 @@
package com.android.server.connectivity
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.NetworkScore.KEEP_CONNECTED_NONE
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
@@ -32,9 +34,13 @@ import kotlin.test.assertNull
class NetworkRankerTest {
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(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