Support "don't ask again" in the avoid bad wifi dialog. am: 165c51c0eb am: aab02df195 am: 63275aba54
am: 3f67cbec46
Change-Id: I60ebe27ec892658c5a051457b13506e59ba33824
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fd9e60d..c793a6e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3241,6 +3241,27 @@
}
/**
+ * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is
+ * only meaningful if the system is configured not to penalize such networks, e.g., if the
+ * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code
+ * NETWORK_AVOID_BAD_WIFI setting is unset}.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
+ *
+ * @param network The network to accept.
+ *
+ * @hide
+ */
+ public void setAvoidUnvalidated(Network network) {
+ try {
+ mService.setAvoidUnvalidated(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resets all connectivity manager settings back to factory defaults.
* @hide
*/
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d48c155..4aabda9 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -161,6 +161,7 @@
void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
+ void setAvoidUnvalidated(in Network network);
int getRestoreDefaultNetworkDelay(int networkType);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f8cf9ab..5c2ca2e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7559,6 +7559,13 @@
/**
* Whether to automatically switch away from wifi networks that lose Internet access.
+ * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
+ * avoids such networks. Valid values are:
+ *
+ * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+ * null: Ask the user whether to switch away from bad wifi.
+ * 1: Avoid bad wifi.
+ *
* @hide
*/
public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 667a4a9..a2d8aa0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -372,6 +372,11 @@
private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
/**
+ * used to specify whether a network should not be penalized when it becomes unvalidated.
+ */
+ private static final int EVENT_SET_AVOID_UNVALIDATED = 35;
+
+ /**
* used to ask the user to confirm a connection to an unvalidated network.
* obj = network
*/
@@ -2720,6 +2725,12 @@
accept ? 1 : 0, always ? 1: 0, network));
}
+ @Override
+ public void setAvoidUnvalidated(Network network) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network));
+ }
+
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -2760,6 +2771,20 @@
}
+ private void handleSetAvoidUnvalidated(Network network) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null || nai.lastValidated) {
+ // Nothing to do. The network either disconnected or revalidated.
+ return;
+ }
+ if (!nai.avoidUnvalidated) {
+ int oldScore = nai.getCurrentScore();
+ nai.avoidUnvalidated = true;
+ rematchAllNetworksAndRequests(nai, oldScore);
+ sendUpdatedScoreToFactories(nai);
+ }
+ }
+
private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
mHandler.sendMessageDelayed(
@@ -2767,28 +2792,31 @@
PROMPT_UNVALIDATED_DELAY_MS);
}
- private boolean mAvoidBadWifi;
+ private boolean mAvoidBadWifi = true;
public boolean avoidBadWifi() {
return mAvoidBadWifi;
}
@VisibleForTesting
- public boolean updateAvoidBadWifi() {
- // There are two modes: either we always automatically avoid unvalidated wifi, or we show a
- // dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI
- // setting. If the setting has no value, then the value is taken from the config value,
- // which can be changed via OEM/carrier overlays.
- //
- // The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit
- // test uses 0 in order to avoid having to mock out fetching the carrier setting.
- int defaultAvoidBadWifi =
- mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi);
- int avoid = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi);
+ /** Whether the device or carrier configuration disables avoiding bad wifi by default. */
+ public boolean configRestrictsAvoidBadWifi() {
+ return mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0;
+ }
+
+ /** Whether we should display a notification when wifi becomes unvalidated. */
+ public boolean shouldNotifyWifiUnvalidated() {
+ return configRestrictsAvoidBadWifi() &&
+ Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.NETWORK_AVOID_BAD_WIFI) == null;
+ }
+
+ private boolean updateAvoidBadWifi() {
+ boolean settingAvoidBadWifi = "1".equals(Settings.Global.getString(
+ mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI));
boolean prev = mAvoidBadWifi;
- mAvoidBadWifi = (avoid == 1);
+ mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
return mAvoidBadWifi != prev;
}
@@ -2841,7 +2869,7 @@
NetworkCapabilities nc = nai.networkCapabilities;
if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
- if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) {
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && shouldNotifyWifiUnvalidated()) {
showValidationNotification(nai, NotificationType.LOST_INTERNET);
}
}
@@ -2924,6 +2952,10 @@
handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
break;
}
+ case EVENT_SET_AVOID_UNVALIDATED: {
+ handleSetAvoidUnvalidated((Network) msg.obj);
+ break;
+ }
case EVENT_PROMPT_UNVALIDATED: {
handlePromptUnvalidated((Network) msg.obj);
break;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index cb4bb88..2a618bc 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -140,12 +140,14 @@
public boolean everValidated;
// The result of the last validation attempt on this network (true if validated, false if not).
- // This bit exists only because we never unvalidate a network once it's been validated, and that
- // is because the network scoring and revalidation code does not (may not?) deal properly with
- // networks becoming unvalidated.
- // TODO: Fix the network scoring code, remove this, and rename everValidated to validated.
public boolean lastValidated;
+ // If true, becoming unvalidated will lower the network's score. This is only meaningful if the
+ // system is configured not to do this for certain networks, e.g., if the
+ // config_networkAvoidBadWifi option is set to 0 and the user has not overridden that via
+ // Settings.Global.NETWORK_AVOID_BAD_WIFI.
+ public boolean avoidUnvalidated;
+
// Whether a captive portal was ever detected on this network.
// This is a sticky bit; once set it is never cleared.
public boolean everCaptivePortalDetected;
@@ -426,8 +428,10 @@
// 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);
- return isWifi && !mConnService.avoidBadWifi() && everValidated;
+ boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
+ networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
+ return isWifi && !avoidBadWifi && everValidated;
}
// Get the current score for this Network. This may be modified from what the
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index de90de8..340be62 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -82,6 +82,7 @@
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
@@ -601,6 +602,7 @@
private class WrappedConnectivityService extends ConnectivityService {
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
+ public boolean configRestrictsAvoidBadWifi;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
@@ -656,6 +658,11 @@
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
}
+ @Override
+ public boolean configRestrictsAvoidBadWifi() {
+ return configRestrictsAvoidBadWifi;
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -2041,8 +2048,48 @@
@SmallTest
public void testAvoidBadWifiSetting() throws Exception {
+ final ContentResolver cr = mServiceContext.getContentResolver();
+ final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
+
+ mService.configRestrictsAvoidBadWifi = false;
+ String[] values = new String[] {null, "0", "1"};
+ for (int i = 0; i < values.length; i++) {
+ Settings.Global.putInt(cr, settingName, 1);
+ mService.updateNetworkAvoidBadWifi();
+ mService.waitForIdle();
+ String msg = String.format("config=false, setting=%s", values[i]);
+ assertTrue(msg, mService.avoidBadWifi());
+ assertFalse(msg, mService.shouldNotifyWifiUnvalidated());
+ }
+
+ mService.configRestrictsAvoidBadWifi = true;
+
+ Settings.Global.putInt(cr, settingName, 0);
+ mService.updateNetworkAvoidBadWifi();
+ mService.waitForIdle();
+ assertFalse(mService.avoidBadWifi());
+ assertFalse(mService.shouldNotifyWifiUnvalidated());
+
+ Settings.Global.putInt(cr, settingName, 1);
+ mService.updateNetworkAvoidBadWifi();
+ mService.waitForIdle();
+ assertTrue(mService.avoidBadWifi());
+ assertFalse(mService.shouldNotifyWifiUnvalidated());
+
+ Settings.Global.putString(cr, settingName, null);
+ mService.updateNetworkAvoidBadWifi();
+ mService.waitForIdle();
+ assertFalse(mService.avoidBadWifi());
+ assertTrue(mService.shouldNotifyWifiUnvalidated());
+ }
+
+ @SmallTest
+ public void testAvoidBadWifi() throws Exception {
ContentResolver cr = mServiceContext.getContentResolver();
+ // Pretend we're on a carrier that restricts switching away from bad wifi.
+ mService.configRestrictsAvoidBadWifi = true;
+
// File a request for cell to ensure it doesn't go down.
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
@@ -2059,8 +2106,8 @@
TestNetworkCallback validatedWifiCallback = new TestNetworkCallback();
mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
- // Takes effect on every rematch.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
+ mService.updateNetworkAvoidBadWifi();
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -2089,7 +2136,42 @@
NET_CAPABILITY_VALIDATED));
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
- // Simulate the user selecting "switch" on the dialog.
+ // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect
+ // that we switch back to cell.
+ mService.configRestrictsAvoidBadWifi = false;
+ mService.updateNetworkAvoidBadWifi();
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCm.getActiveNetwork(), cellNetwork);
+
+ // Switch back to a restrictive carrier.
+ mService.configRestrictsAvoidBadWifi = true;
+ mService.updateNetworkAvoidBadWifi();
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ assertEquals(mCm.getActiveNetwork(), wifiNetwork);
+
+ // Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
+ mCm.setAvoidUnvalidated(wifiNetwork);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
+ NET_CAPABILITY_VALIDATED));
+ assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
+ NET_CAPABILITY_VALIDATED));
+ assertEquals(mCm.getActiveNetwork(), cellNetwork);
+
+ // Disconnect and reconnect wifi to clear the one-time switch above.
+ mWiFiNetworkAgent.disconnect();
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ wifiNetwork = mWiFiNetworkAgent.getNetwork();
+
+ // Fail validation on wifi and expect the dialog to appear.
+ mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+ mCm.reportNetworkConnectivity(wifiNetwork, false);
+ validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+ // Simulate the user selecting "switch" and checking the don't ask again checkbox.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
mService.updateNetworkAvoidBadWifi();
@@ -2101,6 +2183,17 @@
NET_CAPABILITY_VALIDATED));
assertEquals(mCm.getActiveNetwork(), cellNetwork);
+ // Simulate the user turning the cellular fallback setting off and then on.
+ // We switch to wifi and then to cell.
+ Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+ mService.updateNetworkAvoidBadWifi();
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ assertEquals(mCm.getActiveNetwork(), wifiNetwork);
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
+ mService.updateNetworkAvoidBadWifi();
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ assertEquals(mCm.getActiveNetwork(), cellNetwork);
+
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);