Introduce an overlay for actively preferring bad wifi.
This correctly updates when the mcc/mnc change. Test: MultinetworkPolicyTrackerTest Change-Id: I11c7ea7074a15975fb68d39eb3c728778d84a516
This commit is contained in:
@@ -39,6 +39,7 @@ import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.modules.utils.build.SdkLevel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -79,6 +80,29 @@ public class MultinetworkPolicyTracker {
|
||||
private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
private volatile long mTestAllowBadWifiUntilMs = 0;
|
||||
|
||||
/**
|
||||
* Whether to prefer bad wifi to a network that yields to bad wifis, even if it never validated
|
||||
*
|
||||
* This setting only makes sense if the system is configured not to avoid bad wifis, i.e.
|
||||
* if mAvoidBadWifi is true. If it's not, then no network ever yields to bad wifis
|
||||
* ({@see FullScore#POLICY_YIELD_TO_BAD_WIFI}) and this setting has therefore no effect.
|
||||
*
|
||||
* If this is false, when ranking a bad wifi that never validated against cell data (or any
|
||||
* network that yields to bad wifis), the ranker will prefer cell data. It will prefer wifi
|
||||
* if wifi loses validation later. This behavior avoids the device losing internet access when
|
||||
* walking past a wifi network with no internet access.
|
||||
* This is the default behavior up to Android T, but it can be overridden through an overlay
|
||||
* to behave like below.
|
||||
*
|
||||
* If this is true, then in the same scenario, the ranker will prefer cell data until
|
||||
* the wifi completes its first validation attempt (or the attempt times out after
|
||||
* ConnectivityService#PROMPT_UNVALIDATED_DELAY_MS), then it will prefer the wifi even if it
|
||||
* doesn't provide internet access, unless there is a captive portal on that wifi.
|
||||
* This is the behavior in U and above.
|
||||
*/
|
||||
// TODO : implement the behavior.
|
||||
private boolean mActivelyPreferBadWifi;
|
||||
|
||||
// Mainline module can't use internal HandlerExecutor, so add an identical executor here.
|
||||
private static class HandlerExecutor implements Executor {
|
||||
@NonNull
|
||||
@@ -158,6 +182,10 @@ public class MultinetworkPolicyTracker {
|
||||
return mAvoidBadWifi;
|
||||
}
|
||||
|
||||
public boolean getActivelyPreferBadWifi() {
|
||||
return mActivelyPreferBadWifi;
|
||||
}
|
||||
|
||||
// TODO: move this to MultipathPolicyTracker.
|
||||
public int getMeteredMultipathPreference() {
|
||||
return mMeteredMultipathPreference;
|
||||
@@ -179,6 +207,29 @@ public class MultinetworkPolicyTracker {
|
||||
return (getResourcesForActiveSubId().getInteger(id) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the device config prefers bad wifi actively, when it doesn't avoid them
|
||||
*
|
||||
* This is only relevant when the device is configured not to avoid bad wifis. In this
|
||||
* case, "actively" preferring a bad wifi means that the device will switch to a bad
|
||||
* wifi it just connected to, as long as it's not a captive portal.
|
||||
*
|
||||
* On U and above this always returns true. On T and below it reads a configuration option.
|
||||
*/
|
||||
public boolean configActivelyPrefersBadWifi() {
|
||||
// See the definition of config_activelyPreferBadWifi for a description of its meaning.
|
||||
// On U and above, the config is ignored, and bad wifi is always actively preferred.
|
||||
if (SdkLevel.isAtLeastU()) return true;
|
||||
// TODO: use R.integer.config_activelyPreferBadWifi directly
|
||||
final int id = mResources.get().getIdentifier("config_activelyPreferBadWifi",
|
||||
"integer", mResources.getResourcesContext().getPackageName());
|
||||
// On T and below, 1 means to actively prefer bad wifi, 0 means not to prefer
|
||||
// bad wifi (only stay stuck on it if already on there). This implementation treats
|
||||
// any non-0 value like 1, on the assumption that anybody setting it non-zero wants
|
||||
// the newer behavior.
|
||||
return 0 != getResourcesForActiveSubId().getInteger(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
|
||||
* The value works when the time set is more than {@link System.currentTimeMillis()}.
|
||||
@@ -224,9 +275,13 @@ public class MultinetworkPolicyTracker {
|
||||
|
||||
public boolean updateAvoidBadWifi() {
|
||||
final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
|
||||
final boolean prev = mAvoidBadWifi;
|
||||
final boolean prevAvoid = mAvoidBadWifi;
|
||||
mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
|
||||
return mAvoidBadWifi != prev;
|
||||
|
||||
final boolean prevActive = mActivelyPreferBadWifi;
|
||||
mActivelyPreferBadWifi = configActivelyPrefersBadWifi();
|
||||
|
||||
return mAvoidBadWifi != prevAvoid || mActivelyPreferBadWifi != prevActive;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -78,6 +78,27 @@
|
||||
Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
|
||||
<integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
|
||||
|
||||
<!-- Whether the device should actively prefer bad wifi to good cell on Android 12/13.
|
||||
|
||||
This setting only makes sense if the system is configured not to avoid bad wifis
|
||||
(config_networkAvoidBadWifi=0 and Settings.Global.NETWORK_AVOID_BAD_WIFI=IGNORE
|
||||
or PROMPT), otherwise it's not used.
|
||||
|
||||
On Android 12 and 13, if this is 0, when ranking a bad wifi that never validated against
|
||||
validated mobile data, the system will prefer mobile data. It will prefer wifi if wifi
|
||||
loses validation later. This is the default behavior up to Android 13.
|
||||
This behavior avoids the device losing internet access when walking past a wifi network
|
||||
with no internet access.
|
||||
|
||||
If this is 1, then in the same scenario, the system will prefer mobile data until the wifi
|
||||
completes its first validation attempt (or the attempt times out), and after that it
|
||||
will prefer the wifi even if it doesn't provide internet access, unless there is a captive
|
||||
portal on that wifi.
|
||||
|
||||
On Android 14 and above, the behavior is always like 1, regardless of the value of this
|
||||
setting. -->
|
||||
<integer translatable="false" name="config_activelyPreferBadWifi">1</integer>
|
||||
|
||||
<!-- Array of ConnectivityManager.TYPE_xxxx constants for networks that may only
|
||||
be controlled by systemOrSignature apps. -->
|
||||
<integer-array translatable="false" name="config_protectedNetworks">
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<item type="integer" name="config_networkMeteredMultipathPreference"/>
|
||||
<item type="array" name="config_networkSupportedKeepaliveCount"/>
|
||||
<item type="integer" name="config_networkAvoidBadWifi"/>
|
||||
<item type="integer" name="config_activelyPreferBadWifi"/>
|
||||
<item type="array" name="config_protectedNetworks"/>
|
||||
<item type="bool" name="config_vehicleInternalNetworkAlwaysRequested"/>
|
||||
<item type="integer" name="config_networkWakeupPacketMark"/>
|
||||
|
||||
@@ -783,7 +783,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
|
||||
|
||||
private final DnsManager mDnsManager;
|
||||
private final NetworkRanker mNetworkRanker;
|
||||
@VisibleForTesting
|
||||
final NetworkRanker mNetworkRanker;
|
||||
|
||||
private boolean mSystemReady;
|
||||
private Intent mInitialBroadcast;
|
||||
@@ -1417,7 +1418,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
new RequestInfoPerUidCounter(MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1);
|
||||
|
||||
mMetricsLog = logger;
|
||||
mNetworkRanker = new NetworkRanker();
|
||||
final NetworkRequest defaultInternetRequest = createDefaultRequest();
|
||||
mDefaultRequest = new NetworkRequestInfo(
|
||||
Process.myUid(), defaultInternetRequest, null,
|
||||
@@ -1538,6 +1538,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
|
||||
mContext, mHandler, () -> updateAvoidBadWifi());
|
||||
mNetworkRanker =
|
||||
new NetworkRanker(new NetworkRanker.Configuration(activelyPreferBadWifi()));
|
||||
|
||||
mMultinetworkPolicyTracker.start();
|
||||
|
||||
mDnsManager = new DnsManager(mContext, mDnsResolver);
|
||||
@@ -5050,6 +5053,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return mMultinetworkPolicyTracker.getAvoidBadWifi();
|
||||
}
|
||||
|
||||
private boolean activelyPreferBadWifi() {
|
||||
return mMultinetworkPolicyTracker.getActivelyPreferBadWifi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the device should maintain continuous, working connectivity by switching away
|
||||
* from WiFi networks having no connectivity.
|
||||
@@ -5073,6 +5080,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
for (final NetworkOfferInfo noi : offersToUpdate) {
|
||||
updateOfferScore(noi.offer);
|
||||
}
|
||||
mNetworkRanker.setConfiguration(new NetworkRanker.Configuration(activelyPreferBadWifi()));
|
||||
rematchAllNetworksAndRequests();
|
||||
}
|
||||
|
||||
@@ -5088,6 +5096,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
pw.println("Bad Wi-Fi avoidance: " + avoidBadWifi());
|
||||
pw.increaseIndent();
|
||||
pw.println("Config restrict: " + configRestrict);
|
||||
pw.println("Actively prefer: " + activelyPreferBadWifi());
|
||||
|
||||
final String value = mMultinetworkPolicyTracker.getAvoidBadWifiSetting();
|
||||
String description;
|
||||
|
||||
@@ -38,19 +38,42 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.util.MultinetworkPolicyTracker;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A class that knows how to find the best network matching a request out of a list of networks.
|
||||
*/
|
||||
public class NetworkRanker {
|
||||
/**
|
||||
* Home for all configurations of NetworkRanker
|
||||
*/
|
||||
public static final class Configuration {
|
||||
private final boolean mActivelyPreferBadWifi;
|
||||
|
||||
public Configuration(final boolean activelyPreferBadWifi) {
|
||||
this.mActivelyPreferBadWifi = activelyPreferBadWifi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MultinetworkPolicyTracker#getActivelyPreferBadWifi()
|
||||
*/
|
||||
// TODO : implement the behavior.
|
||||
public boolean activelyPreferBadWifi() {
|
||||
return mActivelyPreferBadWifi;
|
||||
}
|
||||
}
|
||||
@NonNull private volatile Configuration mConf;
|
||||
|
||||
// Historically the legacy ints have been 0~100 in principle (though the highest score in
|
||||
// AOSP has always been 90). This is relied on by VPNs that send a legacy score of 101.
|
||||
public static final int LEGACY_INT_MAX = 100;
|
||||
@@ -65,7 +88,22 @@ public class NetworkRanker {
|
||||
NetworkCapabilities getCapsNoCopy();
|
||||
}
|
||||
|
||||
public NetworkRanker() { }
|
||||
public NetworkRanker(@NonNull final Configuration conf) {
|
||||
// Because mConf is volatile, the only way it could be seen null would be an access to it
|
||||
// on some other thread during this constructor. But this is not possible because mConf is
|
||||
// private and `this` doesn't escape this constructor.
|
||||
setConfiguration(conf);
|
||||
}
|
||||
|
||||
public void setConfiguration(@NonNull final Configuration conf) {
|
||||
mConf = Objects.requireNonNull(conf);
|
||||
}
|
||||
|
||||
// There shouldn't be a use case outside of testing
|
||||
@VisibleForTesting
|
||||
public Configuration getConfiguration() {
|
||||
return mConf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the best network satisfying this request among the list of passed networks.
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.test.mock.MockContentResolver
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.connectivity.resources.R
|
||||
import com.android.internal.util.test.FakeSettingsProvider
|
||||
import com.android.modules.utils.build.SdkLevel
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import org.junit.After
|
||||
@@ -50,7 +51,6 @@ import org.mockito.ArgumentMatchers.argThat
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.doCallRealMethod
|
||||
import org.mockito.Mockito.doNothing
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.times
|
||||
@@ -69,7 +69,10 @@ class MultinetworkPolicyTrackerTest {
|
||||
private val resources = mock(Resources::class.java).also {
|
||||
doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier(
|
||||
eq("config_networkAvoidBadWifi"), eq("integer"), any())
|
||||
doReturn(R.integer.config_activelyPreferBadWifi).`when`(it).getIdentifier(
|
||||
eq("config_activelyPreferBadWifi"), eq("integer"), any())
|
||||
doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
|
||||
doReturn(0).`when`(it).getInteger(R.integer.config_activelyPreferBadWifi)
|
||||
}
|
||||
private val telephonyManager = mock(TelephonyManager::class.java)
|
||||
private val subscriptionManager = mock(SubscriptionManager::class.java).also {
|
||||
@@ -122,6 +125,7 @@ class MultinetworkPolicyTrackerTest {
|
||||
|
||||
@Test
|
||||
fun testUpdateAvoidBadWifi() {
|
||||
doReturn(0).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
|
||||
Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
|
||||
assertTrue(tracker.updateAvoidBadWifi())
|
||||
assertFalse(tracker.avoidBadWifi)
|
||||
@@ -129,6 +133,24 @@ class MultinetworkPolicyTrackerTest {
|
||||
doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
|
||||
assertTrue(tracker.updateAvoidBadWifi())
|
||||
assertTrue(tracker.avoidBadWifi)
|
||||
|
||||
if (SdkLevel.isAtLeastU()) {
|
||||
// On U+, the system always prefers bad wifi.
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
} else {
|
||||
assertFalse(tracker.activelyPreferBadWifi)
|
||||
}
|
||||
|
||||
doReturn(1).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
|
||||
if (SdkLevel.isAtLeastU()) {
|
||||
// On U+, this didn't change the setting
|
||||
assertFalse(tracker.updateAvoidBadWifi())
|
||||
} else {
|
||||
// On T-, this must have changed the setting
|
||||
assertTrue(tracker.updateAvoidBadWifi())
|
||||
}
|
||||
// In all cases, now the system actively prefers bad wifi
|
||||
assertTrue(tracker.activelyPreferBadWifi)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1838,7 +1838,10 @@ public class ConnectivityServiceTest {
|
||||
.getIdentifier(eq("network_switch_type_name"), eq("array"), any());
|
||||
doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
|
||||
.getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
|
||||
doReturn(R.integer.config_activelyPreferBadWifi).when(mResources)
|
||||
.getIdentifier(eq("config_activelyPreferBadWifi"), eq("integer"), any());
|
||||
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
|
||||
doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
|
||||
doReturn(true).when(mResources)
|
||||
.getBoolean(R.bool.config_cellular_radio_timesharing_capable);
|
||||
}
|
||||
@@ -5620,6 +5623,24 @@ public class ConnectivityServiceTest {
|
||||
testAvoidBadWifiConfig_controlledBySettings();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivelyPreferBadWifiSetting() throws Exception {
|
||||
doReturn(1).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
|
||||
mPolicyTracker.reevaluate();
|
||||
waitForIdle();
|
||||
assertTrue(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
|
||||
|
||||
doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
|
||||
mPolicyTracker.reevaluate();
|
||||
waitForIdle();
|
||||
if (SdkLevel.isAtLeastU()) {
|
||||
// U+ ignore the setting and always actively prefers bad wifi
|
||||
assertTrue(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
|
||||
} else {
|
||||
assertFalse(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOffersAvoidsBadWifi() throws Exception {
|
||||
// Normal mode : the carrier doesn't restrict moving away from bad wifi.
|
||||
|
||||
@@ -42,7 +42,8 @@ private fun caps(transport: Int) = NetworkCapabilities.Builder().addTransportTyp
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
class NetworkRankerTest {
|
||||
private val mRanker = NetworkRanker()
|
||||
private val mRanker = NetworkRanker(NetworkRanker.Configuration(
|
||||
false /* activelyPreferBadWifi */))
|
||||
|
||||
private class TestScore(private val sc: FullScore, private val nc: NetworkCapabilities)
|
||||
: NetworkRanker.Scoreable {
|
||||
|
||||
Reference in New Issue
Block a user