Merge "Add Validation logging." into mnc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 04a29aa..80476ea 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2381,6 +2381,14 @@
* Status of the request can be followed by listening to the various
* callbacks described in {@link NetworkCallback}. The {@link Network}
* can be used to direct traffic to the network.
+ * <p>It is presently unsupported to request a network with mutable
+ * {@link NetworkCapabilities} such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
+ * as these {@code NetworkCapabilities} represent states that a particular
+ * network may never attain, and whether a network will attain these states
+ * is unknown prior to bringing up the network so the framework does not
+ * know how to go about satisfing a request with these capabilities.
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
*
@@ -2388,6 +2396,8 @@
* @param networkCallback The {@link NetworkCallback} to be utilized for this
* request. Note the callback must not be shared - they
* uniquely specify this request.
+ * @throws IllegalArgumentException if {@code request} specifies any mutable
+ * {@code NetworkCapabilities}.
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
@@ -2469,12 +2479,22 @@
* <p>
* The request may be released normally by calling
* {@link #releaseNetworkRequest(android.app.PendingIntent)}.
+ * <p>It is presently unsupported to request a network with either
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
+ * as these {@code NetworkCapabilities} represent states that a particular
+ * network may never attain, and whether a network will attain these states
+ * is unknown prior to bringing up the network so the framework does not
+ * know how to go about satisfing a request with these capabilities.
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
+ * @throws IllegalArgumentException if {@code request} contains either
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
*/
public void requestNetwork(NetworkRequest request, PendingIntent operation) {
checkPendingIntent(operation);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index bf94b25..658051c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -176,8 +176,14 @@
*/
public static final int NET_CAPABILITY_VALIDATED = 16;
+ /**
+ * Indicates that this network was found to have a captive portal in place last time it was
+ * probed.
+ */
+ public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VALIDATED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_CAPTIVE_PORTAL;
/**
* Adds the given capability to this {@code NetworkCapability} instance.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 84bcf66..47971a1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -23,6 +23,7 @@
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -1577,7 +1578,7 @@
NetworkCapabilities.TRANSPORT_WIFI)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
- 5);
+ 15);
type = ConnectivityManager.TYPE_WIFI;
} else {
// do not track any other networks
@@ -1898,7 +1899,14 @@
if (nai == null) {
loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent");
} else {
- updateCapabilities(nai, (NetworkCapabilities)msg.obj);
+ final NetworkCapabilities networkCapabilities =
+ (NetworkCapabilities)msg.obj;
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
+ networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ Slog.wtf(TAG, "BUG: " + nai + " has stateful capability.");
+ }
+ updateCapabilities(nai, networkCapabilities,
+ NascentState.NOT_JUST_VALIDATED);
}
break;
}
@@ -1985,20 +1993,16 @@
if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
- final boolean validationChanged = (valid != nai.lastValidated);
- nai.lastValidated = valid;
- if (valid) {
- if (DBG) log("Validated " + nai.name());
- nai.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
- if (!nai.everValidated) {
- nai.everValidated = true;
- rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
- ReapUnvalidatedNetworks.REAP);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- sendUpdatedScoreToFactories(nai);
- }
- } else {
- nai.networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+ if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed"));
+ if (valid != nai.lastValidated) {
+ final int oldScore = nai.getCurrentScore();
+ final NascentState nascent = (valid && !nai.everValidated) ?
+ NascentState.JUST_VALIDATED : NascentState.NOT_JUST_VALIDATED;
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(nai, nai.networkCapabilities, nascent);
+ // If score has changed, rebroadcast to NetworkFactories. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -2006,10 +2010,6 @@
android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
-
- if (validationChanged) {
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
}
break;
}
@@ -2022,21 +2022,28 @@
}
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
final int netId = msg.arg2;
- if (msg.arg1 == 0) {
- setProvNotificationVisibleIntent(false, netId, null, 0, null, null);
+ final boolean visible = (msg.arg1 != 0);
+ final NetworkAgentInfo nai;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(netId);
+ }
+ // If captive portal status has changed, update capabilities.
+ if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
+ nai.lastCaptivePortalDetected = visible;
+ nai.everCaptivePortalDetected |= visible;
+ updateCapabilities(nai, nai.networkCapabilities,
+ NascentState.NOT_JUST_VALIDATED);
+ }
+ if (!visible) {
+ setProvNotificationVisibleIntent(false, netId, null, 0, null, null, false);
} else {
- final NetworkAgentInfo nai;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(netId);
- }
if (nai == null) {
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
break;
}
- nai.captivePortalDetected = true;
setProvNotificationVisibleIntent(true, netId, NotificationType.SIGN_IN,
- nai.networkInfo.getType(),nai.networkInfo.getExtraInfo(),
- (PendingIntent)msg.obj);
+ nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(),
+ (PendingIntent)msg.obj, nai.networkMisc.explicitlySelected);
}
break;
}
@@ -2419,7 +2426,7 @@
if (accept != nai.networkMisc.acceptUnvalidated) {
int oldScore = nai.getCurrentScore();
nai.networkMisc.acceptUnvalidated = accept;
- rematchAllNetworksAndRequests(nai, oldScore);
+ rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED);
sendUpdatedScoreToFactories(nai);
}
@@ -2455,7 +2462,7 @@
// 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.captivePortalDetected ||
+ if (nai == null || nai.everValidated || nai.everCaptivePortalDetected ||
!nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
return;
}
@@ -2469,7 +2476,7 @@
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
setProvNotificationVisibleIntent(true, nai.network.netId, NotificationType.NO_INTERNET,
- nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), pendingIntent);
+ nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), pendingIntent, true);
}
private class InternalHandler extends Handler {
@@ -3262,7 +3269,7 @@
// Concatenate the range of types onto the range of NetIDs.
int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
setProvNotificationVisibleIntent(visible, id, NotificationType.SIGN_IN,
- networkType, null, pendingIntent);
+ networkType, null, pendingIntent, false);
}
/**
@@ -3281,11 +3288,12 @@
* we concatenate the range of types with the range of NetIDs.
*/
private void setProvNotificationVisibleIntent(boolean visible, int id,
- NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent) {
+ NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent,
+ boolean highPriority) {
if (DBG) {
log("setProvNotificationVisibleIntent " + notifyType + " visible=" + visible
+ " networkType=" + getNetworkTypeName(networkType)
- + " extraInfo=" + extraInfo);
+ + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
}
Resources r = Resources.getSystem();
@@ -3340,6 +3348,12 @@
.setContentTitle(title)
.setContentText(details)
.setContentIntent(intent)
+ .setLocalOnly(true)
+ .setPriority(highPriority ?
+ Notification.PRIORITY_HIGH :
+ Notification.PRIORITY_DEFAULT)
+ .setDefaults(Notification.DEFAULT_ALL)
+ .setOnlyAlertOnce(true)
.build();
try {
@@ -3600,12 +3614,24 @@
}
}
+ private void ensureImmutableCapabilities(NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ throw new IllegalArgumentException(
+ "Cannot request network with NET_CAPABILITY_VALIDATED");
+ }
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
+ throw new IllegalArgumentException(
+ "Cannot request network with NET_CAPABILITY_CAPTIVE_PORTAL");
+ }
+ }
+
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
+ ensureImmutableCapabilities(networkCapabilities);
if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
throw new IllegalArgumentException("Bad timeout specified");
@@ -3674,6 +3700,7 @@
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
+ ensureImmutableCapabilities(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId());
@@ -4076,18 +4103,35 @@
mNumDnsEntries = last;
}
+ /**
+ * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
+ * augmented with any stateful capabilities implied from {@code networkAgent}
+ * (e.g., validated status and captive portal status).
+ *
+ * @param networkAgent the network having its capabilities updated.
+ * @param networkCapabilities the new network capabilities.
+ * @param nascent indicates whether {@code networkAgent} was validated
+ * (i.e. had everValidated set for the first time) immediately prior to this call.
+ */
private void updateCapabilities(NetworkAgentInfo networkAgent,
- NetworkCapabilities networkCapabilities) {
+ NetworkCapabilities networkCapabilities, NascentState nascent) {
+ // Don't modify caller's NetworkCapabilities.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ if (networkAgent.lastValidated) {
+ networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ } else {
+ networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+ }
+ if (networkAgent.lastCaptivePortalDetected) {
+ networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ } else {
+ networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ }
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
synchronized (networkAgent) {
networkAgent.networkCapabilities = networkCapabilities;
}
- if (networkAgent.lastValidated) {
- networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
- // There's no need to remove the capability if we think the network is unvalidated,
- // because NetworkAgents don't set the validated capability.
- }
- rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
+ rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore(), nascent);
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
}
@@ -4431,15 +4475,21 @@
}
}
- // Attempt to rematch all Networks with NetworkRequests. This may result in Networks
- // being disconnected.
- // If only one Network's score or capabilities have been modified since the last time
- // this function was called, pass this Network in via the "changed" arugment, otherwise
- // pass null.
- // If only one Network has been changed but its NetworkCapabilities have not changed,
- // pass in the Network's score (from getCurrentScore()) prior to the change via
- // "oldScore", otherwise pass changed.getCurrentScore() or 0 if "changed" is null.
- private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) {
+ /**
+ * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * being disconnected.
+ * @param changed If only one Network's score or capabilities have been modified since the last
+ * time this function was called, pass this Network in this argument, otherwise pass
+ * null.
+ * @param oldScore If only one Network has been changed but its NetworkCapabilities have not
+ * changed, pass in the Network's score (from getCurrentScore()) prior to the change via
+ * this argument, otherwise pass {@code changed.getCurrentScore()} or 0 if
+ * {@code changed} is {@code null}. This is because NetworkCapabilities influence a
+ * network's score.
+ * @param nascent indicates if {@code changed} has just been validated.
+ */
+ private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore,
+ NascentState nascent) {
// TODO: This may get slow. The "changed" parameter is provided for future optimization
// to avoid the slowness. It is not simply enough to process just "changed", for
// example in the case where "changed"'s score decreases and another network should begin
@@ -4448,9 +4498,9 @@
// Optimization: Only reprocess "changed" if its score improved. This is safe because it
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
// rematchNetworkAndRequests() handles.
- if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, NascentState.NOT_JUST_VALIDATED,
- ReapUnvalidatedNetworks.REAP);
+ if (changed != null &&
+ (oldScore < changed.getCurrentScore() || nascent == NascentState.JUST_VALIDATED)) {
+ rematchNetworkAndRequests(changed, nascent, ReapUnvalidatedNetworks.REAP);
} else {
for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) {
rematchNetworkAndRequests((NetworkAgentInfo)i.next(),
@@ -4577,7 +4627,7 @@
final int oldScore = nai.getCurrentScore();
nai.setCurrentScore(score);
- rematchAllNetworksAndRequests(nai, oldScore);
+ rematchAllNetworksAndRequests(nai, oldScore, NascentState.NOT_JUST_VALIDATED);
sendUpdatedScoreToFactories(nai);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 3bf1183..51c6628 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -45,6 +45,7 @@
// This Network object is always valid.
public final Network network;
public LinkProperties linkProperties;
+ // This should only be modified via ConnectivityService.updateCapabilities().
public NetworkCapabilities networkCapabilities;
public final NetworkMonitor networkMonitor;
public final NetworkMisc networkMisc;
@@ -66,7 +67,10 @@
// Whether a captive portal was ever detected on this network.
// This is a sticky bit; once set it is never cleared.
- public boolean captivePortalDetected;
+ public boolean everCaptivePortalDetected;
+
+ // Whether a captive portal was found during the last network validation attempt.
+ public boolean lastCaptivePortalDetected;
// This represents the last score received from the NetworkAgent.
private int currentScore;
@@ -174,7 +178,8 @@
"created{" + created + "} " +
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
- "captivePortalDetected{" + captivePortalDetected + "} " +
+ "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
+ "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " +
"}";
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5d56d4a..74ba404 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -113,18 +113,18 @@
public void recordSuccess(String msg) {
maybeFixupTimes();
+ result = SUCCEEDED + ": " + msg;
if (mCountDownLatch != null) {
mCountDownLatch.countDown();
}
- result = SUCCEEDED + ": " + msg;
}
public void recordFailure(String msg) {
maybeFixupTimes();
+ result = FAILED + ": " + msg;
if (mCountDownLatch != null) {
mCountDownLatch.countDown();
}
- result = FAILED + ": " + msg;
}
private void maybeFixupTimes() {
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 712db09..fb8a5bb 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
@@ -792,6 +793,30 @@
handlerThread.quit();
}
+ @LargeTest
+ public void testNoMutableNetworkRequests() throws Exception {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ try {
+ mCm.requestNetwork(builder.build(), new NetworkCallback());
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ try {
+ mCm.requestNetwork(builder.build(), pendingIntent);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ builder = new NetworkRequest.Builder();
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ try {
+ mCm.requestNetwork(builder.build(), new NetworkCallback());
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ try {
+ mCm.requestNetwork(builder.build(), pendingIntent);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
// @Override
// public void tearDown() throws Exception {