private static final int EVENT_REQUEST_LINKPROPERTIES = 32;
private static final int EVENT_REQUEST_NETCAPABILITIES = 33;
+ /**
+ * Used internally to (re)configure avoid bad wifi setting.
+ */
+ private static final int EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI = 34;
+
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+
+ // Watch for whether to automatically switch away from wifi networks that lose Internet
+ // access.
+ mSettingsObserver.observe(
+ Settings.Global.getUriFor(Settings.Global.NETWORK_AVOID_BAD_WIFI),
+ EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI);
}
private synchronized int nextNetworkRequestId() {
if (nai != null) {
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean wasValidated = nai.lastValidated;
if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
(msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
if (valid != nai.lastValidated) {
NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, redirectUrlBundle);
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
+ }
}
break;
}
@VisibleForTesting
public boolean avoidBadWifi() {
+ // 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.
int defaultAvoidBadWifi =
mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi);
int avoid = Settings.Global.getInt(mContext.getContentResolver(),
return avoid == 1;
}
+ private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) {
+ final String action;
+ switch (type) {
+ case NO_INTERNET:
+ action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
+ break;
+ case LOST_INTERNET:
+ action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown notification type " + type);
+ return;
+ }
+
+ Intent intent = new Intent(action);
+ intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.wifi.WifiNoInternetDialog");
+
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+ mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, true);
+ }
+
private void handlePromptUnvalidated(Network network) {
if (VDBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
!nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
return;
}
+ showValidationNotification(nai, NotificationType.NO_INTERNET);
+ }
- Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
- intent.setData(Uri.fromParts("netId", Integer.toString(network.netId), null));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClassName("com.android.settings",
- "com.android.settings.wifi.WifiNoInternetDialog");
-
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+ private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
+ NetworkCapabilities nc = nai.networkCapabilities;
+ if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
- mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai, null,
- pendingIntent, true);
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) {
+ showValidationNotification(nai, NotificationType.LOST_INTERNET);
+ }
}
private class InternalHandler extends Handler {
handleMobileDataAlwaysOn();
break;
}
+ case EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI: {
+ rematchAllNetworksAndRequests(null, 0);
+ break;
+ }
case EVENT_REQUEST_LINKPROPERTIES:
handleRequestLinkProperties((NetworkRequest) msg.obj, msg.arg1);
break;
} else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
// If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
// mark it as no longer satisfying "nri". Because networks are processed by
- // rematchAllNetworkAndRequests() in descending score order, "currentNetwork" will
+ // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
// match "newNetwork" before this loop will encounter a "currentNetwork" with higher
// score than "newNetwork" and where "currentNetwork" no longer satisfies "nri".
// This means this code doesn't have to handle the case where "currentNetwork" no
}
}
}
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
}
@VisibleForTesting
public class NetworkNotificationManager {
- public static enum NotificationType { SIGN_IN, NO_INTERNET, NETWORK_SWITCH };
+ public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
private static final String NOTIFICATION_ID = "Connectivity.Notification";
* @param id an identifier that uniquely identifies this notification. This must match
* between show and hide calls. We use the NetID value but for legacy callers
* we concatenate the range of types with the range of NetIDs.
- * @param nai the network with which the notification is associated. For a SIGN_IN or
- * NO_INTERNET notification, this is the network we're connecting to. For a
+ * @param nai the network with which the notification is associated. For a SIGN_IN, NO_INTERNET,
+ * or LOST_INTERNET notification, this is the network we're connecting to. For a
* NETWORK_SWITCH notification it's the network that we switched from. When this network
* disconnects the notification is removed.
* @param switchToNai for a NETWORK_SWITCH notification, the network we are switching to. Null
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, 0);
details = r.getString(R.string.wifi_no_internet_detailed);
+ } else if (notifyType == NotificationType.LOST_INTERNET &&
+ transportType == TRANSPORT_WIFI) {
+ title = r.getString(R.string.wifi_no_internet, 0);
+ details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.SIGN_IN) {
switch (transportType) {
case TRANSPORT_WIFI: