Support "don't ask again" in the avoid bad wifi dialog.

am: c65750c8a0

Change-Id: Ic6a1917a83e2d847bd673790e2c36f7316ca7acd
This commit is contained in:
Lorenzo Colitti
2016-09-20 17:14:50 +00:00
committed by android-build-merger
5 changed files with 174 additions and 23 deletions

View File

@@ -3197,6 +3197,27 @@ public class ConnectivityManager {
} }
} }
/**
* 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. * Resets all connectivity manager settings back to factory defaults.
* @hide * @hide

View File

@@ -161,6 +161,7 @@ interface IConnectivityManager
void releaseNetworkRequest(in NetworkRequest networkRequest); void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always); void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
int getRestoreDefaultNetworkDelay(int networkType); int getRestoreDefaultNetworkDelay(int networkType);

View File

@@ -371,6 +371,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/ */
private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28; 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. * used to ask the user to confirm a connection to an unvalidated network.
* obj = network * obj = network
@@ -2712,6 +2717,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
accept ? 1 : 0, always ? 1: 0, network)); 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) { private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network + if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always); " accept=" + accept + " always=" + always);
@@ -2752,6 +2763,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
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) { private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network); if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
mHandler.sendMessageDelayed( mHandler.sendMessageDelayed(
@@ -2759,28 +2784,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
PROMPT_UNVALIDATED_DELAY_MS); PROMPT_UNVALIDATED_DELAY_MS);
} }
private boolean mAvoidBadWifi; private boolean mAvoidBadWifi = true;
public boolean avoidBadWifi() { public boolean avoidBadWifi() {
return mAvoidBadWifi; return mAvoidBadWifi;
} }
@VisibleForTesting @VisibleForTesting
public boolean updateAvoidBadWifi() { /** Whether the device or carrier configuration disables avoiding bad wifi by default. */
// There are two modes: either we always automatically avoid unvalidated wifi, or we show a public boolean configRestrictsAvoidBadWifi() {
// dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI return mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0;
// setting. If the setting has no value, then the value is taken from the config value, }
// which can be changed via OEM/carrier overlays.
// /** Whether we should display a notification when wifi becomes unvalidated. */
// The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit public boolean shouldNotifyWifiUnvalidated() {
// test uses 0 in order to avoid having to mock out fetching the carrier setting. return configRestrictsAvoidBadWifi() &&
int defaultAvoidBadWifi = Settings.Global.getString(mContext.getContentResolver(),
mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi); Settings.Global.NETWORK_AVOID_BAD_WIFI) == null;
int avoid = Settings.Global.getInt(mContext.getContentResolver(), }
Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi);
private boolean updateAvoidBadWifi() {
boolean settingAvoidBadWifi = "1".equals(Settings.Global.getString(
mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI));
boolean prev = mAvoidBadWifi; boolean prev = mAvoidBadWifi;
mAvoidBadWifi = (avoid == 1); mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
return mAvoidBadWifi != prev; return mAvoidBadWifi != prev;
} }
@@ -2833,7 +2861,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkCapabilities nc = nai.networkCapabilities; NetworkCapabilities nc = nai.networkCapabilities;
if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc); 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); showValidationNotification(nai, NotificationType.LOST_INTERNET);
} }
} }
@@ -2911,6 +2939,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0); handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
break; break;
} }
case EVENT_SET_AVOID_UNVALIDATED: {
handleSetAvoidUnvalidated((Network) msg.obj);
break;
}
case EVENT_PROMPT_UNVALIDATED: { case EVENT_PROMPT_UNVALIDATED: {
handlePromptUnvalidated((Network) msg.obj); handlePromptUnvalidated((Network) msg.obj);
break; break;

View File

@@ -140,12 +140,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
public boolean everValidated; public boolean everValidated;
// The result of the last validation attempt on this network (true if validated, false if not). // 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; 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. // Whether a captive portal was ever detected on this network.
// This is a sticky bit; once set it is never cleared. // This is a sticky bit; once set it is never cleared.
public boolean everCaptivePortalDetected; public boolean everCaptivePortalDetected;
@@ -426,8 +428,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Return true on devices configured to ignore score penalty for wifi networks // Return true on devices configured to ignore score penalty for wifi networks
// that become unvalidated (b/31075769). // that become unvalidated (b/31075769).
private boolean ignoreWifiUnvalidationPenalty() { private boolean ignoreWifiUnvalidationPenalty() {
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
return isWifi && !mConnService.avoidBadWifi() && everValidated; 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 // Get the current score for this Network. This may be modified from what the

View File

@@ -82,6 +82,7 @@ import com.android.server.net.NetworkPinner;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -601,6 +602,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private class WrappedConnectivityService extends ConnectivityService { private class WrappedConnectivityService extends ConnectivityService {
private WrappedNetworkMonitor mLastCreatedNetworkMonitor; private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public boolean configRestrictsAvoidBadWifi;
public WrappedConnectivityService(Context context, INetworkManagementService netManager, public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager, INetworkStatsService statsService, INetworkPolicyManager policyManager,
@@ -656,6 +658,11 @@ public class ConnectivityServiceTest extends AndroidTestCase {
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj); return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
} }
@Override
public boolean configRestrictsAvoidBadWifi() {
return configRestrictsAvoidBadWifi;
}
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() { public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor; return mLastCreatedNetworkMonitor;
} }
@@ -2035,8 +2042,48 @@ public class ConnectivityServiceTest extends AndroidTestCase {
@SmallTest @SmallTest
public void testAvoidBadWifiSetting() throws Exception { 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(); 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. // File a request for cell to ensure it doesn't go down.
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder() final NetworkRequest cellRequest = new NetworkRequest.Builder()
@@ -2053,8 +2100,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); TestNetworkCallback validatedWifiCallback = new TestNetworkCallback();
mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
// Takes effect on every rematch.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0); Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
mService.updateNetworkAvoidBadWifi();
// Bring up validated cell. // Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
@@ -2083,7 +2130,42 @@ public class ConnectivityServiceTest extends AndroidTestCase {
NET_CAPABILITY_VALIDATED)); NET_CAPABILITY_VALIDATED));
assertEquals(mCm.getActiveNetwork(), wifiNetwork); 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); Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
mService.updateNetworkAvoidBadWifi(); mService.updateNetworkAvoidBadWifi();
@@ -2095,6 +2177,17 @@ public class ConnectivityServiceTest extends AndroidTestCase {
NET_CAPABILITY_VALIDATED)); NET_CAPABILITY_VALIDATED));
assertEquals(mCm.getActiveNetwork(), cellNetwork); 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. // If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);