[NS07] Add the rest of the scoring policy
Bug: 167544279 Test: FrameworksNetTests Merged-In: I3c2563d4ae4e3715d0c6270344ba8f7ef067872f Merged-In: I5ea44a94ac6f16486274e9091f15a84734db2341 Change-Id: I5ea44a94ac6f16486274e9091f15a84734db2341 (cherry-picked from ag/13988828)
This commit is contained in:
@@ -52,11 +52,27 @@ public final class NetworkScore implements Parcelable {
|
||||
public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
|
||||
|
||||
// Agent-managed policies
|
||||
// TODO : add them here, starting from 1
|
||||
// This network should lose to a wifi that has ever been validated
|
||||
// NOTE : temporarily this policy is managed by ConnectivityService, because of legacy. The
|
||||
// legacy design has this bit global to the system and tacked on WiFi which means it will affect
|
||||
// networks from carriers who don't want it and non-carrier networks, which is bad for users.
|
||||
// The S design has this on mobile networks only, so this can be fixed eventually ; as CS
|
||||
// doesn't know what carriers need this bit, the initial S implementation will continue to
|
||||
// affect other carriers but will at least leave non-mobile networks alone. Eventually Telephony
|
||||
// should set this on networks from carriers that require it.
|
||||
/** @hide */
|
||||
public static final int MIN_AGENT_MANAGED_POLICY = 0;
|
||||
public static final int POLICY_YIELD_TO_BAD_WIFI = 1;
|
||||
// This network is primary for this transport.
|
||||
/** @hide */
|
||||
public static final int MAX_AGENT_MANAGED_POLICY = -1;
|
||||
public static final int POLICY_TRANSPORT_PRIMARY = 2;
|
||||
// This network is exiting : it will likely disconnect in a few seconds.
|
||||
/** @hide */
|
||||
public static final int POLICY_EXITING = 3;
|
||||
|
||||
/** @hide */
|
||||
public static final int MIN_AGENT_MANAGED_POLICY = POLICY_YIELD_TO_BAD_WIFI;
|
||||
/** @hide */
|
||||
public static final int MAX_AGENT_MANAGED_POLICY = POLICY_EXITING;
|
||||
|
||||
// Bitmask of all the policies applied to this score.
|
||||
private final long mPolicies;
|
||||
@@ -98,6 +114,60 @@ public final class NetworkScore implements Parcelable {
|
||||
return 0 != (mPolicies & (1L << policy));
|
||||
}
|
||||
|
||||
/**
|
||||
* To the exclusive usage of FullScore
|
||||
* @hide
|
||||
*/
|
||||
public long getPolicies() {
|
||||
return mPolicies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this network should yield to a previously validated wifi gone bad.
|
||||
*
|
||||
* If this policy is set, other things being equal, the device will prefer a previously
|
||||
* validated WiFi even if this network is validated and the WiFi is not.
|
||||
* If this policy is not set, the device prefers the validated network.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
|
||||
// In the mean time this is handled by Connectivity in a backward-compatible manner.
|
||||
public boolean shouldYieldToBadWifi() {
|
||||
return hasPolicy(POLICY_YIELD_TO_BAD_WIFI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this network is primary for this transport.
|
||||
*
|
||||
* When multiple networks of the same transport are active, the device prefers the ones that
|
||||
* are primary. This is meant in particular for DS-DA devices with a user setting to choose the
|
||||
* default SIM card, or for WiFi STA+STA and make-before-break cases.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO : @SystemApi
|
||||
public boolean isTransportPrimary() {
|
||||
return hasPolicy(POLICY_TRANSPORT_PRIMARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this network is exiting.
|
||||
*
|
||||
* If this policy is set, the device will expect this network to disconnect within seconds.
|
||||
* It will try to migrate to some other network if any is available, policy permitting, to
|
||||
* avoid service disruption.
|
||||
* This is useful in particular when a good cellular network is available and WiFi is getting
|
||||
* weak and risks disconnecting soon. The WiFi network should be marked as exiting so that
|
||||
* the device will prefer the reliable mobile network over this soon-to-be-lost WiFi.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO : @SystemApi
|
||||
public boolean isExiting() {
|
||||
return hasPolicy(POLICY_EXITING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Score(" + mLegacyInt + ")";
|
||||
@@ -137,6 +207,7 @@ public final class NetworkScore implements Parcelable {
|
||||
private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
|
||||
private int mLegacyInt = INVALID_LEGACY_INT;
|
||||
private int mKeepConnectedReason = KEEP_CONNECTED_NONE;
|
||||
private int mPolicies = 0;
|
||||
|
||||
/**
|
||||
* Sets the legacy int for this score.
|
||||
@@ -152,6 +223,75 @@ public final class NetworkScore implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set for a network that should never be preferred to a wifi that has ever been validated
|
||||
*
|
||||
* If this policy is set, other things being equal, the device will prefer a previously
|
||||
* validated WiFi even if this network is validated and the WiFi is not.
|
||||
* If this policy is not set, the device prefers the validated network.
|
||||
*
|
||||
* @return this builder
|
||||
* @hide
|
||||
*/
|
||||
// TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
|
||||
// In the mean time this is handled by Connectivity in a backward-compatible manner.
|
||||
@NonNull
|
||||
public Builder setShouldYieldToBadWifi(final boolean val) {
|
||||
if (val) {
|
||||
mPolicies |= (1L << POLICY_YIELD_TO_BAD_WIFI);
|
||||
} else {
|
||||
mPolicies &= ~(1L << POLICY_YIELD_TO_BAD_WIFI);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set for a network that is primary for this transport.
|
||||
*
|
||||
* When multiple networks of the same transport are active, the device prefers the ones that
|
||||
* are primary. This is meant in particular for DS-DA devices with a user setting to choose
|
||||
* the default SIM card, or for WiFi STA+STA and make-before-break cases.
|
||||
*
|
||||
* @return this builder
|
||||
* @hide
|
||||
*/
|
||||
// TODO : @SystemApi
|
||||
@NonNull
|
||||
public Builder setTransportPrimary(final boolean val) {
|
||||
if (val) {
|
||||
mPolicies |= (1L << POLICY_TRANSPORT_PRIMARY);
|
||||
} else {
|
||||
mPolicies &= ~(1L << POLICY_TRANSPORT_PRIMARY);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set for a network that will likely disconnect in a few seconds.
|
||||
*
|
||||
* If this policy is set, the device will expect this network to disconnect within seconds.
|
||||
* It will try to migrate to some other network if any is available, policy permitting, to
|
||||
* avoid service disruption.
|
||||
* This is useful in particular when a good cellular network is available and WiFi is
|
||||
* getting weak and risks disconnecting soon. The WiFi network should be marked as exiting
|
||||
* so that the device will prefer the reliable mobile network over this soon-to-be-lost
|
||||
* WiFi.
|
||||
*
|
||||
* @return this builder
|
||||
* @hide
|
||||
*/
|
||||
// TODO : @SystemApi
|
||||
@NonNull
|
||||
public Builder setExiting(final boolean val) {
|
||||
if (val) {
|
||||
mPolicies |= (1L << POLICY_EXITING);
|
||||
} else {
|
||||
mPolicies &= ~(1L << POLICY_EXITING);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keep-connected reason.
|
||||
*
|
||||
@@ -169,7 +309,7 @@ public final class NetworkScore implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public NetworkScore build() {
|
||||
return new NetworkScore(mLegacyInt, POLICY_NONE, mKeepConnectedReason);
|
||||
return new NetworkScore(mLegacyInt, mPolicies, mKeepConnectedReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1383,7 +1383,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
|
||||
|
||||
mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
|
||||
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
|
||||
mContext, mHandler, () -> updateAvoidBadWifi());
|
||||
mMultinetworkPolicyTracker.start();
|
||||
|
||||
mDnsManager = new DnsManager(mContext, mDnsResolver);
|
||||
@@ -4477,8 +4477,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return avoidBadWifi();
|
||||
}
|
||||
|
||||
// TODO : this function is now useless.
|
||||
private void rematchForAvoidBadWifiUpdate() {
|
||||
private void updateAvoidBadWifi() {
|
||||
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
|
||||
nai.updateScoreForNetworkAgentConfigUpdate();
|
||||
}
|
||||
rematchAllNetworksAndRequests();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,13 @@
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
|
||||
import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
|
||||
import static android.net.NetworkScore.POLICY_EXITING;
|
||||
import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
|
||||
import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
@@ -52,7 +56,8 @@ public class FullScore {
|
||||
POLICY_IS_VALIDATED,
|
||||
POLICY_IS_VPN,
|
||||
POLICY_EVER_USER_SELECTED,
|
||||
POLICY_ACCEPT_UNVALIDATED
|
||||
POLICY_ACCEPT_UNVALIDATED,
|
||||
POLICY_IS_UNMETERED
|
||||
})
|
||||
public @interface Policy {
|
||||
}
|
||||
@@ -77,12 +82,22 @@ public class FullScore {
|
||||
/** @hide */
|
||||
public static final int POLICY_ACCEPT_UNVALIDATED = 60;
|
||||
|
||||
// This network is unmetered. {@see NetworkCapabilities.NET_CAPABILITY_NOT_METERED}.
|
||||
/** @hide */
|
||||
public static final int POLICY_IS_UNMETERED = 59;
|
||||
|
||||
// To help iterate when printing
|
||||
@VisibleForTesting
|
||||
static final int MIN_CS_MANAGED_POLICY = POLICY_ACCEPT_UNVALIDATED;
|
||||
static final int MIN_CS_MANAGED_POLICY = POLICY_IS_UNMETERED;
|
||||
@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
|
||||
// there are more than 32 bits handled on either side.
|
||||
private static final int EXTERNAL_POLICIES_MASK = 0x0000FFFF;
|
||||
|
||||
@VisibleForTesting
|
||||
static @NonNull String policyNameOf(final int policy) {
|
||||
switch (policy) {
|
||||
@@ -90,6 +105,10 @@ public class FullScore {
|
||||
case POLICY_IS_VPN: return "IS_VPN";
|
||||
case POLICY_EVER_USER_SELECTED: return "EVER_USER_SELECTED";
|
||||
case POLICY_ACCEPT_UNVALIDATED: return "ACCEPT_UNVALIDATED";
|
||||
case POLICY_IS_UNMETERED: return "IS_UNMETERED";
|
||||
case POLICY_YIELD_TO_BAD_WIFI: return "YIELD_TO_BAD_WIFI";
|
||||
case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY";
|
||||
case POLICY_EXITING: return "EXITING";
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown policy : " + policy);
|
||||
}
|
||||
@@ -112,15 +131,23 @@ 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
|
||||
* @return an FullScore that is appropriate to use for ranking.
|
||||
* @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad
|
||||
* @return a FullScore that is appropriate to use for ranking.
|
||||
*/
|
||||
// TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
|
||||
// telephony factory, so that it depends on the carrier. For now this is handled by
|
||||
// connectivity for backward compatibility.
|
||||
public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
|
||||
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
|
||||
return withPolicies(score.getLegacyInt(), score.getKeepConnectedReason(),
|
||||
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
|
||||
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),
|
||||
config.explicitlySelected,
|
||||
config.acceptUnvalidated);
|
||||
config.acceptUnvalidated,
|
||||
yieldToBadWiFi);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,12 +169,17 @@ public class FullScore {
|
||||
final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
|
||||
// VPN transports are known in advance.
|
||||
final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
|
||||
// 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;
|
||||
// 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.
|
||||
final boolean acceptUnvalidated = false;
|
||||
return withPolicies(score.getLegacyInt(), KEEP_CONNECTED_NONE,
|
||||
mayValidate, vpn, everUserSelected, acceptUnvalidated);
|
||||
// Don't assume clinging to bad wifi
|
||||
final boolean yieldToBadWiFi = false;
|
||||
return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
|
||||
mayValidate, vpn, unmetered, everUserSelected, acceptUnvalidated, yieldToBadWiFi);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,26 +189,39 @@ public class FullScore {
|
||||
* @param config the NetworkAgentConfig of the network
|
||||
* @return a score with the policies from the arguments reset
|
||||
*/
|
||||
// TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
|
||||
// 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) {
|
||||
return withPolicies(mLegacyInt, mKeepConnectedReason,
|
||||
@NonNull final NetworkAgentConfig config, final boolean avoidBadWiFi) {
|
||||
return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason,
|
||||
caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
caps.hasTransport(TRANSPORT_VPN),
|
||||
caps.hasCapability(NET_CAPABILITY_NOT_METERED),
|
||||
config.explicitlySelected,
|
||||
config.acceptUnvalidated);
|
||||
config.acceptUnvalidated,
|
||||
avoidBadWiFi);
|
||||
}
|
||||
|
||||
// TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
|
||||
// telephony factory, so that it depends on the carrier. For now this is handled by
|
||||
// connectivity for backward compatibility.
|
||||
private static FullScore withPolicies(@NonNull final int legacyInt,
|
||||
final long externalPolicies,
|
||||
@KeepConnectedReason final int keepConnectedReason,
|
||||
final boolean isValidated,
|
||||
final boolean isVpn,
|
||||
final boolean isUnmetered,
|
||||
final boolean everUserSelected,
|
||||
final boolean acceptUnvalidated) {
|
||||
return new FullScore(legacyInt,
|
||||
(isValidated ? 1L << POLICY_IS_VALIDATED : 0)
|
||||
final boolean acceptUnvalidated,
|
||||
final boolean yieldToBadWiFi) {
|
||||
return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK)
|
||||
| (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
|
||||
| (isVpn ? 1L << POLICY_IS_VPN : 0)
|
||||
| (isUnmetered ? 1L << POLICY_IS_UNMETERED : 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),
|
||||
keepConnectedReason);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.transportNamesOf;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -362,9 +363,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
linkProperties = lp;
|
||||
networkCapabilities = nc;
|
||||
networkAgentConfig = config;
|
||||
setScore(score); // uses members networkCapabilities and networkAgentConfig
|
||||
clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
|
||||
mConnService = connService;
|
||||
setScore(score); // uses members connService, networkCapabilities and networkAgentConfig
|
||||
clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
this.factorySerialNumber = factorySerialNumber;
|
||||
@@ -706,7 +707,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
@NonNull final NetworkCapabilities nc) {
|
||||
final NetworkCapabilities oldNc = networkCapabilities;
|
||||
networkCapabilities = nc;
|
||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
|
||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
|
||||
final NetworkMonitorManager nm = mNetworkMonitor;
|
||||
if (nm != null) {
|
||||
nm.notifyNetworkCapabilitiesChanged(nc);
|
||||
@@ -714,6 +715,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
return oldNc;
|
||||
}
|
||||
|
||||
private boolean yieldToBadWiFi() {
|
||||
// Only cellular networks yield to bad wifi
|
||||
return networkCapabilities.hasTransport(TRANSPORT_CELLULAR) && !mConnService.avoidBadWifi();
|
||||
}
|
||||
|
||||
public ConnectivityService connService() {
|
||||
return mConnService;
|
||||
}
|
||||
@@ -884,15 +890,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
return isVPN();
|
||||
}
|
||||
|
||||
// Return true on devices configured to ignore score penalty for wifi networks
|
||||
// that become unvalidated (b/31075769).
|
||||
private boolean ignoreWifiUnvalidationPenalty() {
|
||||
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
|
||||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||
boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
|
||||
return isWifi && !avoidBadWifi && everValidated;
|
||||
}
|
||||
|
||||
public FullScore getScore() {
|
||||
return mScore;
|
||||
}
|
||||
@@ -913,7 +910,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
* Mix-in the ConnectivityService-managed bits in the score.
|
||||
*/
|
||||
public void setScore(final NetworkScore score) {
|
||||
mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig);
|
||||
mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig,
|
||||
yieldToBadWiFi());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -922,7 +920,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
* Call this after updating the network agent config.
|
||||
*/
|
||||
public void updateScoreForNetworkAgentConfigUpdate() {
|
||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
|
||||
mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1085,7 +1083,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
return "NetworkAgentInfo{"
|
||||
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
|
||||
+ networkInfo.toShortString() + "} "
|
||||
+ " Score{" + getCurrentScore() + "} "
|
||||
+ mScore + " "
|
||||
+ (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
|
||||
+ (everValidated ? " everValidated" : "")
|
||||
+ (lastValidated ? " lastValidated" : "")
|
||||
|
||||
@@ -56,7 +56,7 @@ class FullScoreTest {
|
||||
if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
|
||||
if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
}.build()
|
||||
return mixInScore(nc, nac)
|
||||
return mixInScore(nc, nac, false /* avoidBadWifi */)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user