Merge "Check if network has partial connectivity"

am: 2d3cc4577a

Change-Id: I3e94029fe067a1a22acc640a0df6a87720572b94
This commit is contained in:
Lucas Lin
2019-03-12 01:28:50 -07:00
committed by android-build-merger
9 changed files with 394 additions and 48 deletions

View File

@@ -426,6 +426,16 @@ public class ConnectivityManager {
public static final String ACTION_PROMPT_LOST_VALIDATION =
"android.net.conn.PROMPT_LOST_VALIDATION";
/**
* Action used to display a dialog that asks the user whether to stay connected to a network
* that has not validated. This intent is used to start the dialog in settings via
* startActivity.
*
* @hide
*/
public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY =
"android.net.conn.PROMPT_PARTIAL_CONNECTIVITY";
/**
* Invalid tethering type.
* @see #startTethering(int, boolean, OnStartTetheringCallback)
@@ -4018,7 +4028,7 @@ public class ConnectivityManager {
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
try {
mService.setAcceptUnvalidated(network, accept, always);
@@ -4027,6 +4037,29 @@ public class ConnectivityManager {
}
}
/**
* Informs the system whether it should consider the network as validated even if it only has
* partial connectivity. If {@code accept} is true, then the network will be considered as
* validated even if connectivity is only partial. If {@code always} is true, then the choice
* is remembered, so that the next time the user connects to this network, the system will
* switch to it.
*
* @param network The network to accept.
* @param accept Whether to consider the network as validated even if it has partial
* connectivity.
* @param always Whether to remember this choice in the future.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
try {
mService.setAcceptPartialConnectivity(network, accept, always);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* 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
@@ -4037,7 +4070,7 @@ public class ConnectivityManager {
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void setAvoidUnvalidated(Network network) {
try {
mService.setAvoidUnvalidated(network);

View File

@@ -176,6 +176,7 @@ interface IConnectivityManager
void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAcceptPartialConnectivity(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);

View File

@@ -143,7 +143,8 @@ public final class NetworkCapabilities implements Parcelable {
NET_CAPABILITY_NOT_CONGESTED,
NET_CAPABILITY_NOT_SUSPENDED,
NET_CAPABILITY_OEM_PAID,
NET_CAPABILITY_MCX
NET_CAPABILITY_MCX,
NET_CAPABILITY_PARTIAL_CONNECTIVITY,
})
public @interface NetCapability { }
@@ -304,8 +305,15 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int NET_CAPABILITY_MCX = 23;
/**
* Indicates that this network was tested to only provide partial connectivity.
* @hide
*/
@SystemApi
public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_MCX;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -320,7 +328,8 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_FOREGROUND)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED);
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -374,6 +383,15 @@ public final class NetworkCapabilities implements Parcelable {
(1 << NET_CAPABILITY_SUPL) |
(1 << NET_CAPABILITY_WIFI_P2P);
/**
* Capabilities that are managed by ConnectivityService.
*/
private static final long CONNECTIVITY_MANAGED_CAPABILITIES =
(1 << NET_CAPABILITY_VALIDATED)
| (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
| (1 << NET_CAPABILITY_FOREGROUND)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
* Multiple capabilities may be applied sequentially. Note that when searching
@@ -507,6 +525,14 @@ public final class NetworkCapabilities implements Parcelable {
&& ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
}
/**
* Check if this NetworkCapabilities has system managed capabilities or not.
* @hide
*/
public boolean hasConnectivityManagedCapability() {
return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
}
/** Note this method may result in having the same capability in wanted and unwanted lists. */
private void combineNetCapabilities(NetworkCapabilities nc) {
this.mNetworkCapabilities |= nc.mNetworkCapabilities;
@@ -1599,31 +1625,32 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static String capabilityNameOf(@NetCapability int capability) {
switch (capability) {
case NET_CAPABILITY_MMS: return "MMS";
case NET_CAPABILITY_SUPL: return "SUPL";
case NET_CAPABILITY_DUN: return "DUN";
case NET_CAPABILITY_FOTA: return "FOTA";
case NET_CAPABILITY_IMS: return "IMS";
case NET_CAPABILITY_CBS: return "CBS";
case NET_CAPABILITY_WIFI_P2P: return "WIFI_P2P";
case NET_CAPABILITY_IA: return "IA";
case NET_CAPABILITY_RCS: return "RCS";
case NET_CAPABILITY_XCAP: return "XCAP";
case NET_CAPABILITY_EIMS: return "EIMS";
case NET_CAPABILITY_NOT_METERED: return "NOT_METERED";
case NET_CAPABILITY_INTERNET: return "INTERNET";
case NET_CAPABILITY_NOT_RESTRICTED: return "NOT_RESTRICTED";
case NET_CAPABILITY_TRUSTED: return "TRUSTED";
case NET_CAPABILITY_NOT_VPN: return "NOT_VPN";
case NET_CAPABILITY_VALIDATED: return "VALIDATED";
case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
case NET_CAPABILITY_OEM_PAID: return "OEM_PAID";
case NET_CAPABILITY_MCX: return "MCX";
default: return Integer.toString(capability);
case NET_CAPABILITY_MMS: return "MMS";
case NET_CAPABILITY_SUPL: return "SUPL";
case NET_CAPABILITY_DUN: return "DUN";
case NET_CAPABILITY_FOTA: return "FOTA";
case NET_CAPABILITY_IMS: return "IMS";
case NET_CAPABILITY_CBS: return "CBS";
case NET_CAPABILITY_WIFI_P2P: return "WIFI_P2P";
case NET_CAPABILITY_IA: return "IA";
case NET_CAPABILITY_RCS: return "RCS";
case NET_CAPABILITY_XCAP: return "XCAP";
case NET_CAPABILITY_EIMS: return "EIMS";
case NET_CAPABILITY_NOT_METERED: return "NOT_METERED";
case NET_CAPABILITY_INTERNET: return "INTERNET";
case NET_CAPABILITY_NOT_RESTRICTED: return "NOT_RESTRICTED";
case NET_CAPABILITY_TRUSTED: return "TRUSTED";
case NET_CAPABILITY_NOT_VPN: return "NOT_VPN";
case NET_CAPABILITY_VALIDATED: return "VALIDATED";
case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
case NET_CAPABILITY_OEM_PAID: return "OEM_PAID";
case NET_CAPABILITY_MCX: return "MCX";
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
default: return Integer.toString(capability);
}
}

View File

@@ -51,6 +51,12 @@ public class NetworkMisc implements Parcelable {
*/
public boolean acceptUnvalidated;
/**
* Whether the user explicitly set that this network should be validated even if presence of
* only partial internet connectivity.
*/
public boolean acceptPartialConnectivity;
/**
* Set to avoid surfacing the "Sign in to network" notification.
* if carrier receivers/apps are registered to handle the carrier-specific provisioning

View File

@@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
@@ -34,6 +35,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
@@ -488,6 +490,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
public static final int EVENT_TIMEOUT_NOTIFICATION = 44;
/**
* Used to specify whether a network should be used even if connectivity is partial.
* arg1 = whether to accept the network if its connectivity is partial (1 for true or 0 for
* false)
* arg2 = whether to remember this choice in the future (1 for true or 0 for false)
* obj = network
*/
private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
@@ -2487,9 +2498,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) ||
networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) {
if (networkCapabilities.hasConnectivityManagedCapability()) {
Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
@@ -2514,6 +2523,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
nai.networkMisc.explicitlySelected = true;
nai.networkMisc.acceptUnvalidated = msg.arg1 == 1;
// Mark the network as temporarily accepting partial connectivity so that it
// will be validated (and possibly become default) even if it only provides
// partial internet access. Note that if user connects to partial connectivity
// and choose "don't ask again", then wifi disconnected by some reasons(maybe
// out of wifi coverage) and if the same wifi is available again, the device
// will auto connect to this wifi even though the wifi has "no internet".
// TODO: Evaluate using a separate setting in IpMemoryStore.
nai.networkMisc.acceptPartialConnectivity = msg.arg1 == 1;
break;
}
case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2531,6 +2548,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
final boolean partialConnectivity =
(msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY)
// If user accepts partial connectivity network, NetworkMonitor
// will skip https probing. It will make partial connectivity
// network becomes valid. But user still need to know this
// network is limited. So, it's needed to refer to
// acceptPartialConnectivity to add
// NET_CAPABILITY_PARTIAL_CONNECTIVITY into NetworkCapabilities
// of this network. So that user can see "Limited connection"
// in the settings.
|| (nai.networkMisc.acceptPartialConnectivity
&& nai.partialConnectivity);
// Once a network is determined to have partial connectivity, it cannot
// go back to full connectivity without a disconnect.
final boolean partialConnectivityChange =
(partialConnectivity && !nai.partialConnectivity);
final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
@@ -2539,6 +2573,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.captivePortalLoginNotified = true;
showNetworkNotification(nai, NotificationType.LOGGED_IN);
}
// If this network has just connected and partial connectivity has just been
// detected, tell NetworkMonitor if the user accepted partial connectivity on a
// previous connect.
if ((msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY)
&& nai.networkMisc.acceptPartialConnectivity) {
try {
nai.networkMonitor().notifyAcceptPartialConnectivity();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
@@ -2568,6 +2613,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET);
}
} else if (partialConnectivityChange) {
nai.partialConnectivity = partialConnectivity;
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -2606,7 +2654,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (!visible) {
// Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
// notifications belong to the same network may be cleared unexpected.
// notifications belong to the same network may be cleared unexpectedly.
mNotifier.clearNotification(netId, NotificationType.SIGN_IN);
mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH);
} else {
@@ -3192,14 +3240,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceConnectivityInternalPermission();
enforceNetworkStackSettingsOrSetup();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
encodeBool(accept), encodeBool(always), network));
}
@Override
public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
enforceNetworkStackSettingsOrSetup();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY,
encodeBool(accept), encodeBool(always), network));
}
@Override
public void setAvoidUnvalidated(Network network) {
enforceConnectivityInternalPermission();
enforceNetworkStackSettingsOrSetup();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network));
}
@@ -3225,6 +3280,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (accept != nai.networkMisc.acceptUnvalidated) {
int oldScore = nai.getCurrentScore();
nai.networkMisc.acceptUnvalidated = accept;
// If network becomes partial connectivity and user already accepted to use this
// network, we should respect the user's option and don't need to popup the
// PARTIAL_CONNECTIVITY notification to user again.
nai.networkMisc.acceptPartialConnectivity = accept;
rematchAllNetworksAndRequests(nai, oldScore);
sendUpdatedScoreToFactories(nai);
}
@@ -3243,6 +3302,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void handleSetAcceptPartialConnectivity(Network network, boolean accept,
boolean always) {
if (DBG) {
log("handleSetAcceptPartialConnectivity network=" + network + " accept=" + accept
+ " always=" + always);
}
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) {
// Nothing to do.
return;
}
if (nai.lastValidated) {
// The network validated while the dialog box was up. Take no action.
return;
}
if (accept != nai.networkMisc.acceptPartialConnectivity) {
nai.networkMisc.acceptPartialConnectivity = accept;
}
// TODO: Use the current design or save the user choice into IpMemoryStore.
if (always) {
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED, encodeBool(accept));
}
if (!accept) {
// Tell the NetworkAgent to not automatically reconnect to the network.
nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
// Tear down the network.
teardownUnneededNetwork(nai);
} else {
try {
nai.networkMonitor().notifyAcceptPartialConnectivity();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
private void handleSetAvoidUnvalidated(Network network) {
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null || nai.lastValidated) {
@@ -3413,6 +3514,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
case LOST_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
break;
case PARTIAL_CONNECTIVITY:
action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
break;
default:
Slog.wtf(TAG, "Unknown notification type " + type);
return;
@@ -3435,22 +3539,36 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
// Only prompt if the network is unvalidated and was explicitly selected by the user, and if
// we haven't already been told to switch to it regardless of whether it validated or not.
// Also don't prompt on captive portals because we're already prompting the user to sign in.
if (nai == null || nai.everValidated || nai.everCaptivePortalDetected ||
!nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
// Only prompt if the network is unvalidated or network has partial internet connectivity
// and was explicitly selected by the user, and if we haven't already been told to switch
// to it regardless of whether it validated or not. Also don't prompt on captive portals
// because we're already prompting the user to sign in.
if (nai == null || nai.everValidated || nai.everCaptivePortalDetected
|| !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated
|| nai.networkMisc.acceptPartialConnectivity) {
return;
}
showNetworkNotification(nai, NotificationType.NO_INTERNET);
// TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
// NetworkMonitor detects the network is partial connectivity. Need to change the design to
// popup the notification immediately when the network is partial connectivity.
if (nai.partialConnectivity) {
// Treat PARTIAL_CONNECTIVITY as NO_INTERNET temporary until Settings has been updated.
// TODO: Need to change back to PARTIAL_CONNECTIVITY when Settings part is merged.
showNetworkNotification(nai, NotificationType.NO_INTERNET);
} else {
showNetworkNotification(nai, NotificationType.NO_INTERNET);
}
}
private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
NetworkCapabilities nc = nai.networkCapabilities;
if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return;
}
if (mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
showNetworkNotification(nai, NotificationType.LOST_INTERNET);
}
}
@@ -3541,6 +3659,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleSetAcceptUnvalidated(network, toBool(msg.arg1), toBool(msg.arg2));
break;
}
case EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY: {
Network network = (Network) msg.obj;
handleSetAcceptPartialConnectivity(network, toBool(msg.arg1),
toBool(msg.arg2));
break;
}
case EVENT_SET_AVOID_UNVALIDATED: {
handleSetAvoidUnvalidated((Network) msg.obj);
break;
@@ -5465,6 +5589,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
if (nai.partialConnectivity) {
newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
} else {
newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
}
return newNc;
}

View File

@@ -156,6 +156,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// last detected.
public boolean captivePortalLoginNotified;
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
@@ -592,6 +595,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
// TODO: Print shorter members first and only print the boolean variable which value is true
// to improve readability.
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} "
+ "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} "
@@ -604,6 +609,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
+ "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
+ "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
+ "captivePortalLoginNotified{" + captivePortalLoginNotified + "} "
+ "partialConnectivity{" + partialConnectivity + "} "
+ "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+ "clat{" + clatd + "} "
+ "}";
}

View File

@@ -47,8 +47,9 @@ public class NetworkNotificationManager {
LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN);
LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
public final int eventId;
@@ -169,11 +170,18 @@ public class NetworkNotificationManager {
CharSequence details;
int icon = getIcon(transportType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, 0);
title = r.getString(R.string.wifi_no_internet,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.network_partial_connectivity,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
details = r.getString(R.string.network_partial_connectivity_detailed);
} else if (notifyType == NotificationType.LOST_INTERNET &&
transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, 0);
title = r.getString(R.string.wifi_no_internet,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.SIGN_IN) {
switch (transportType) {
@@ -316,6 +324,8 @@ public class NetworkNotificationManager {
}
switch (t) {
case SIGN_IN:
return 5;
case PARTIAL_CONNECTIVITY:
return 4;
case NO_INTERNET:
return 3;

View File

@@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -27,6 +28,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
@@ -334,6 +336,24 @@ public class NetworkCapabilitiesTest {
assertTrue(request.satisfiedByNetworkCapabilities(network));
}
@Test
public void testConnectivityManagedCapabilities() {
NetworkCapabilities nc = new NetworkCapabilities();
assertFalse(nc.hasConnectivityManagedCapability());
// Check every single system managed capability.
nc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
assertTrue(nc.hasConnectivityManagedCapability());
nc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
nc.addCapability(NET_CAPABILITY_FOREGROUND);
assertTrue(nc.hasConnectivityManagedCapability());
nc.removeCapability(NET_CAPABILITY_FOREGROUND);
nc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
assertTrue(nc.hasConnectivityManagedCapability());
nc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
nc.addCapability(NET_CAPABILITY_VALIDATED);
assertTrue(nc.hasConnectivityManagedCapability());
}
@Test
public void testEqualsNetCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities();

View File

@@ -28,6 +28,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
@@ -43,6 +44,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
@@ -442,6 +444,11 @@ public class ConnectivityServiceTest {
mNmValidationRedirectUrl = redirectUrl;
}
void setNetworkPartial() {
mNmValidationResult = NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
mNmValidationRedirectUrl = null;
}
MockNetworkAgent(int transport) {
this(transport, new LinkProperties());
}
@@ -484,6 +491,7 @@ public class ConnectivityServiceTest {
try {
doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
doAnswer(validateAnswer).when(mNetworkMonitor).notifyAcceptPartialConnectivity();
} catch (RemoteException e) {
fail(e.getMessage());
}
@@ -669,6 +677,11 @@ public class ConnectivityServiceTest {
connect(false);
}
public void connectWithPartialConnectivity() {
setNetworkPartial();
connect(false);
}
public void suspend() {
mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -2497,6 +2510,106 @@ public class ConnectivityServiceTest {
verifyActiveNetwork(TRANSPORT_CELLULAR);
}
@Test
public void testPartialConnectivity() {
// Register network callback.
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
.build();
TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(request, callback);
// Bring up validated mobile data.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi with partial connectivity.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
// Mobile data should be the default network.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
// If the user chooses yes to use this partial connectivity wifi, switch the default
// network to wifi and check if wifi becomes valid or not.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
false /* always */);
// With https probe disabled, NetworkMonitor should pass the network validation with http
// probe.
mWiFiNetworkAgent.setNetworkValid();
waitForIdle();
try {
verify(mWiFiNetworkAgent.mNetworkMonitor,
timeout(TIMEOUT_MS).times(1)).notifyAcceptPartialConnectivity();
} catch (RemoteException e) {
fail(e.getMessage());
}
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED,
mWiFiNetworkAgent);
assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Disconnect and reconnect wifi with partial connectivity again.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
// Mobile data should be the default network.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// If the user chooses no, disconnect wifi immediately.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */,
false /* always */);
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// If user accepted partial connectivity before, and device reconnects to that network
// again, but now the network has full connectivity. The network shouldn't contain
// NET_CAPABILITY_PARTIAL_CONNECTIVITY.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
// acceptUnvalidated is also used as setting for accepting partial networks.
mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
// Wifi should be the default network.
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// If user accepted partial connectivity before, and now the device reconnects to the
// partial connectivity network. The network should be valid and contain
// NET_CAPABILITY_PARTIAL_CONNECTIVITY.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: If the user accepted partial connectivity, we shouldn't switch to wifi until
// NetworkMonitor detects partial connectivity
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.setNetworkValid();
waitForIdle();
try {
verify(mWiFiNetworkAgent.mNetworkMonitor,
timeout(TIMEOUT_MS).times(1)).notifyAcceptPartialConnectivity();
} catch (RemoteException e) {
fail(e.getMessage());
}
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
// Wifi should be the default network.
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
}
@Test
public void testCaptivePortal() {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();