am 682fd6ae: am a991c66c: Merge "Fix missing onLost NetworkCallbacks when network loses capability" into mnc-dev

* commit '682fd6aedea87e345f033b489ef9f6008bd19c30':
  Fix missing onLost NetworkCallbacks when network loses capability
This commit is contained in:
Paul Jensen
2015-07-30 02:36:45 +00:00
committed by Android Git Automerger
5 changed files with 232 additions and 88 deletions

View File

@ -792,6 +792,7 @@ public final class NetworkCapabilities implements Parcelable {
case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break; case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break;
case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break; case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break;
case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break; case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break;
case NET_CAPABILITY_CAPTIVE_PORTAL: capabilities += "CAPTIVE_PORTAL"; break;
} }
if (++i < types.length) capabilities += "&"; if (++i < types.length) capabilities += "&";
} }

View File

@ -2171,6 +2171,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDefaultInetConditionPublished = 0; mDefaultInetConditionPublished = 0;
} }
notifyIfacesChanged(); notifyIfacesChanged();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
// by other networks that are already connected. Perhaps that can be done by
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai, mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK); ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
@ -2288,7 +2292,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// is currently satisfying the request. This is desirable when // is currently satisfying the request. This is desirable when
// cellular ends up validating but WiFi does not. // cellular ends up validating but WiFi does not.
// 2. Unvalidated WiFi will not be reaped when validated cellular // 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satsifying the request. This is desirable when // is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular. // WiFi ends up validating and out scoring cellular.
mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
nai.getCurrentScoreAsValidated())) { nai.getCurrentScoreAsValidated())) {
@ -3866,10 +3870,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest. // satisfies mDefaultRequest.
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties( new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest); mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
synchronized (this) { synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady; nai.networkMonitor.systemReady = mSystemReady;
} }
@ -4265,8 +4269,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>(); ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>();
if (VDBG) log(" network has: " + newNetwork.networkCapabilities); if (VDBG) log(" network has: " + newNetwork.networkCapabilities);
for (NetworkRequestInfo nri : mNetworkRequests.values()) { for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (newNetwork == currentNetwork) { final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) {
if (VDBG) { if (VDBG) {
log("Network " + newNetwork.name() + " was already satisfying" + log("Network " + newNetwork.name() + " was already satisfying" +
" request " + nri.request.requestId + ". No change."); " request " + nri.request.requestId + ". No change.");
@ -4277,7 +4282,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// check if it satisfies the NetworkCapabilities // check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request); if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (newNetwork.satisfies(nri.request)) { if (satisfies) {
if (!nri.isRequest) { if (!nri.isRequest) {
// This is not a request, it's a callback listener. // This is not a request, it's a callback listener.
// Add it to newNetwork regardless of score. // Add it to newNetwork regardless of score.
@ -4320,6 +4325,37 @@ public class ConnectivityService extends IConnectivityManager.Stub
oldDefaultNetwork = currentNetwork; oldDefaultNetwork = currentNetwork;
} }
} }
} else if (newNetwork.networkRequests.get(nri.request.requestId) != null) {
// 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
// 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
// longer satisfies "nri" when "currentNetwork" does not equal "newNetwork".
if (DBG) {
log("Network " + newNetwork.name() + " stopped satisfying" +
" request " + nri.request.requestId);
}
newNetwork.networkRequests.remove(nri.request.requestId);
if (currentNetwork == newNetwork) {
mNetworkForRequestId.remove(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
if (nri.isRequest == true) {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
newNetwork.name() +
" without updating mNetworkForRequestId or factories!");
}
}
// TODO: technically, sending CALLBACK_LOST here is
// incorrect if nri is a request (not a listen) and there
// is a replacement network currently connected that can
// satisfy it. However, the only capability that can both
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
// so this code is only incorrect for a network that loses
// the TRUSTED capability, which is a rare case.
callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST);
} }
} }
// Linger any networks that are no longer needed. // Linger any networks that are no longer needed.
@ -4338,41 +4374,41 @@ public class ConnectivityService extends IConnectivityManager.Stub
unlinger(nai); unlinger(nai);
} }
} }
if (isNewDefault) {
// Notify system services that this network is up.
makeDefault(newNetwork);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in
// a second if it's held. The second pause is to allow apps
// to reconnect over the new network
if (mNetTransitionWakeLock.isHeld()) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(
EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
mNetTransitionWakeLockSerialNumber, 0),
1000);
}
}
}
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
if (isNewDefault) {
// Maintain the illusion: since the legacy API only
// understands one network at a time, we must pretend
// that the current default network disconnected before
// the new one connected.
if (oldDefaultNetwork != null) {
mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
oldDefaultNetwork, true);
}
mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
notifyLockdownVpn(newNetwork);
}
if (keep) { if (keep) {
if (isNewDefault) {
// Notify system services that this network is up.
makeDefault(newNetwork);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in
// a second if it's held. The second pause is to allow apps
// to reconnect over the new network
if (mNetTransitionWakeLock.isHeld()) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(
EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
mNetTransitionWakeLockSerialNumber, 0),
1000);
}
}
}
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
if (isNewDefault) {
// Maintain the illusion: since the legacy API only
// understands one network at a time, we must pretend
// that the current default network disconnected before
// the new one connected.
if (oldDefaultNetwork != null) {
mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
oldDefaultNetwork, true);
}
mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
notifyLockdownVpn(newNetwork);
}
// Notify battery stats service about this network, both the normal // Notify battery stats service about this network, both the normal
// interface and any stacked links. // interface and any stacked links.
// TODO: Avoid redoing this; this must only be done once when a network comes online. // TODO: Avoid redoing this; this must only be done once when a network comes online.
@ -4791,4 +4827,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
} }
} }
@VisibleForTesting
public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo nai, NetworkRequest defaultRequest) {
return new NetworkMonitor(context, handler, nai, defaultRequest);
}
} }

View File

@ -30,6 +30,7 @@ import android.os.Messenger;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.util.AsyncChannel; import com.android.internal.util.AsyncChannel;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkMonitor;
import java.util.ArrayList; import java.util.ArrayList;
@ -51,7 +52,7 @@ import java.util.Comparator;
// ConnectivityService will tell netd to create the network and immediately transition to // ConnectivityService will tell netd to create the network and immediately transition to
// state #3. // state #3.
// 3. registered, created, connected, unvalidated // 3. registered, created, connected, unvalidated
// If this network can satsify the default NetworkRequest, then NetworkMonitor will // If this network can satisfy the default NetworkRequest, then NetworkMonitor will
// probe for Internet connectivity. // probe for Internet connectivity.
// If this network cannot satisfy the default NetworkRequest, it will immediately be // If this network cannot satisfy the default NetworkRequest, it will immediately be
// transitioned to state #4. // transitioned to state #4.
@ -164,7 +165,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest) { NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
this.messenger = messenger; this.messenger = messenger;
asyncChannel = ac; asyncChannel = ac;
network = net; network = net;
@ -172,7 +173,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp; linkProperties = lp;
networkCapabilities = nc; networkCapabilities = nc;
currentScore = score; currentScore = score;
networkMonitor = new NetworkMonitor(context, handler, this, defaultRequest); networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc; networkMisc = misc;
} }

View File

@ -349,7 +349,7 @@ public class NetworkMonitor extends StateMachine {
// Being in the ValidatedState State indicates a Network is: // Being in the ValidatedState State indicates a Network is:
// - Successfully validated, or // - Successfully validated, or
// - Wanted "as is" by the user, or // - Wanted "as is" by the user, or
// - Does not satsify the default NetworkRequest and so validation has been skipped. // - Does not satisfy the default NetworkRequest and so validation has been skipped.
private class ValidatedState extends State { private class ValidatedState extends State {
@Override @Override
public void enter() { public void enter() {
@ -558,7 +558,7 @@ public class NetworkMonitor extends StateMachine {
// Being in the LingeringState State indicates a Network's validated bit is true and it once // Being in the LingeringState State indicates a Network's validated bit is true and it once
// was the highest scoring Network satisfying a particular NetworkRequest, but since then // was the highest scoring Network satisfying a particular NetworkRequest, but since then
// another Network satsified the NetworkRequest with a higher score and hence this Network // another Network satisfied the NetworkRequest with a higher score and hence this Network
// is "lingered" for a fixed period of time before it is disconnected. This period of time // is "lingered" for a fixed period of time before it is disconnected. This period of time
// allows apps to wrap up communication and allows for seamless reactivation if the other // allows apps to wrap up communication and allows for seamless reactivation if the other
// higher scoring Network happens to disconnect. // higher scoring Network happens to disconnect.
@ -633,7 +633,8 @@ public class NetworkMonitor extends StateMachine {
* Do a URL fetch on a known server to see if we get the data we expect. * Do a URL fetch on a known server to see if we get the data we expect.
* Returns HTTP response code. * Returns HTTP response code.
*/ */
private int isCaptivePortal() { @VisibleForTesting
protected int isCaptivePortal() {
if (!mIsCaptivePortalCheckEnabled) return 204; if (!mIsCaptivePortalCheckEnabled) return 204;
HttpURLConnection urlConnection = null; HttpURLConnection urlConnection = null;

View File

@ -81,6 +81,7 @@ import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log; import android.util.Log;
import android.util.LogPrinter; import android.util.LogPrinter;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkMonitor;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -118,7 +119,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private INetworkPolicyManager mPolicyService; private INetworkPolicyManager mPolicyService;
private BroadcastInterceptingContext mServiceContext; private BroadcastInterceptingContext mServiceContext;
private ConnectivityService mService; private WrappedConnectivityService mService;
private ConnectivityManager mCm; private ConnectivityManager mCm;
private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mWiFiNetworkAgent;
private MockNetworkAgent mCellNetworkAgent; private MockNetworkAgent mCellNetworkAgent;
@ -148,6 +149,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
} }
private class MockNetworkAgent { private class MockNetworkAgent {
private final WrappedNetworkMonitor mWrappedNetworkMonitor;
private final NetworkInfo mNetworkInfo; private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities; private final NetworkCapabilities mNetworkCapabilities;
private final Thread mThread; private final Thread mThread;
@ -172,6 +174,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
throw new UnsupportedOperationException("unimplemented network type"); throw new UnsupportedOperationException("unimplemented network type");
} }
final ConditionVariable initComplete = new ConditionVariable(); final ConditionVariable initComplete = new ConditionVariable();
final ConditionVariable networkMonitorAvailable = mService.getNetworkMonitorCreatedCV();
mThread = new Thread() { mThread = new Thread() {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
@ -186,6 +189,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
}; };
mThread.start(); mThread.start();
waitFor(initComplete); waitFor(initComplete);
waitFor(networkMonitorAvailable);
mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor();
} }
public void adjustScore(int change) { public void adjustScore(int change) {
@ -211,44 +216,46 @@ public class ConnectivityServiceTest extends AndroidTestCase {
assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE); assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)); assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
// To pretend network is validated, we transition it to the CONNECTED state without
// NET_CAPABILITY_INTERNET so NetworkMonitor doesn't bother trying to validate and
// just rubber stamps it as validated. Afterwards we add NET_CAPABILITY_INTERNET so
// the network can satisfy the default request.
NetworkCallback callback = null; NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable(); final ConditionVariable validatedCv = new ConditionVariable();
if (validated) { if (validated) {
// If we connect a network without INTERNET capability, it'll get reaped. mWrappedNetworkMonitor.gen204ProbeResult = 204;
// Prevent the reaping by adding a NetworkRequest.
NetworkRequest request = new NetworkRequest.Builder() NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(mNetworkCapabilities.getTransportTypes()[0]) .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
.build(); .build();
callback = new NetworkCallback() { callback = new NetworkCallback() {
public void onCapabilitiesChanged(Network network, public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) { NetworkCapabilities networkCapabilities) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { if (network.equals(getNetwork()) &&
networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
validatedCv.open(); validatedCv.open();
} }
} }
}; };
mCm.requestNetwork(request, callback); mCm.registerNetworkCallback(request, callback);
} else {
mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
} }
addCapability(NET_CAPABILITY_INTERNET);
connectWithoutInternet(); connectWithoutInternet();
if (validated) { if (validated) {
// Wait for network to validate. // Wait for network to validate.
waitFor(validatedCv); waitFor(validatedCv);
mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET); mWrappedNetworkMonitor.gen204ProbeResult = 500;
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
} }
if (callback != null) mCm.unregisterNetworkCallback(callback); if (callback != null) mCm.unregisterNetworkCallback(callback);
} }
public void connectWithCaptivePortal() {
mWrappedNetworkMonitor.gen204ProbeResult = 200;
connect(false);
waitFor(new Criteria() { public boolean get() {
NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
mWrappedNetworkMonitor.gen204ProbeResult = 500;
}
public void disconnect() { public void disconnect() {
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo); mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@ -261,14 +268,18 @@ public class ConnectivityServiceTest extends AndroidTestCase {
public ConditionVariable getDisconnectedCV() { public ConditionVariable getDisconnectedCV() {
return mDisconnected; return mDisconnected;
} }
public WrappedNetworkMonitor getWrappedNetworkMonitor() {
return mWrappedNetworkMonitor;
}
} }
private static class MockNetworkFactory extends NetworkFactory { private static class MockNetworkFactory extends NetworkFactory {
final ConditionVariable mNetworkStartedCV = new ConditionVariable(); private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); private final ConditionVariable mNetworkRequestedCV = new ConditionVariable();
final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); private final ConditionVariable mNetworkReleasedCV = new ConditionVariable();
final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
public MockNetworkFactory(Looper looper, Context context, String logTag, public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) { NetworkCapabilities filter) {
@ -328,7 +339,26 @@ public class ConnectivityServiceTest extends AndroidTestCase {
} }
} }
// NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
private class WrappedNetworkMonitor extends NetworkMonitor {
// HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
public int gen204ProbeResult = 500;
public WrappedNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) {
super(context, handler, networkAgentInfo, defaultRequest);
}
@Override
protected int isCaptivePortal() {
return gen204ProbeResult;
}
}
private class WrappedConnectivityService extends ConnectivityService { private class WrappedConnectivityService extends ConnectivityService {
private final ConditionVariable mNetworkMonitorCreated = new ConditionVariable();
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public WrappedConnectivityService(Context context, INetworkManagementService netManager, public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) { INetworkStatsService statsService, INetworkPolicyManager policyManager) {
super(context, netManager, statsService, policyManager); super(context, netManager, statsService, policyManager);
@ -360,6 +390,25 @@ public class ConnectivityServiceTest extends AndroidTestCase {
return netId; return netId;
} }
} }
@Override
public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo nai, NetworkRequest defaultRequest) {
final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(context, handler, nai,
defaultRequest);
mLastCreatedNetworkMonitor = monitor;
mNetworkMonitorCreated.open();
return monitor;
}
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
public ConditionVariable getNetworkMonitorCreatedCV() {
mNetworkMonitorCreated.close();
return mNetworkMonitorCreated;
}
} }
private interface Criteria { private interface Criteria {
@ -586,29 +635,29 @@ public class ConnectivityServiceTest extends AndroidTestCase {
@LargeTest @LargeTest
public void testUnlingeringDoesNotValidate() throws Exception { public void testUnlingeringDoesNotValidate() throws Exception {
// Test bringing up unvalidated cellular. // Test bringing up unvalidated WiFi.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
cv = waitForConnectivityBroadcasts(2); ConditionVariable cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(false);
waitFor(cv); waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI); verifyActiveNetwork(TRANSPORT_WIFI);
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED)); NET_CAPABILITY_VALIDATED));
// Test WiFi disconnect. // Test bringing up validated cellular.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
cv = waitForConnectivityBroadcasts(2); cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.disconnect(); mCellNetworkAgent.connect(true);
waitFor(cv); waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR); verifyActiveNetwork(TRANSPORT_CELLULAR);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test cellular disconnect.
cv = waitForConnectivityBroadcasts(2);
mCellNetworkAgent.disconnect();
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Unlingering a network should not cause it to be marked as validated. // Unlingering a network should not cause it to be marked as validated.
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED)); NET_CAPABILITY_VALIDATED));
} }
@ -846,12 +895,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
cellCv = cellNetworkCallback.getConditionVariable(); cellCv = cellNetworkCallback.getConditionVariable();
wifiCv = wifiNetworkCallback.getConditionVariable(); wifiCv = wifiNetworkCallback.getConditionVariable();
// Our method for faking successful validation generates an additional callback, so wait
// for broadcast instead.
cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true); mCellNetworkAgent.connect(true);
waitFor(cv);
waitFor(cellCv); waitFor(cellCv);
assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback()); assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback());
assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback());
@ -868,12 +913,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
cellCv = cellNetworkCallback.getConditionVariable(); cellCv = cellNetworkCallback.getConditionVariable();
wifiCv = wifiNetworkCallback.getConditionVariable(); wifiCv = wifiNetworkCallback.getConditionVariable();
// Our method for faking successful validation generates an additional callback, so wait
// for broadcast instead.
cv = waitForConnectivityBroadcasts(1);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
waitFor(cv);
waitFor(wifiCv); waitFor(wifiCv);
assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback()); assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback());
waitFor(cellCv); waitFor(cellCv);
@ -1075,6 +1116,63 @@ public class ConnectivityServiceTest extends AndroidTestCase {
verifyActiveNetwork(TRANSPORT_CELLULAR); verifyActiveNetwork(TRANSPORT_CELLULAR);
} }
@LargeTest
public void testCaptivePortal() {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
final TestNetworkCallback validatedCallback = new TestNetworkCallback();
final NetworkRequest validatedRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_VALIDATED).build();
mCm.registerNetworkCallback(validatedRequest, validatedCallback);
ConditionVariable validatedCv = validatedCallback.getConditionVariable();
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
ConditionVariable cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithCaptivePortal();
waitFor(cv);
assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback());
// Take down network.
// Expect onLost callback.
cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent.disconnect();
waitFor(cv);
assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback());
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithCaptivePortal();
waitFor(cv);
assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback());
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
cv = captivePortalCallback.getConditionVariable();
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
waitFor(cv);
assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback());
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
waitFor(validatedCv);
assertEquals(CallbackState.AVAILABLE, validatedCallback.getLastCallback());
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
validatedCv = validatedCallback.getConditionVariable();
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
waitFor(validatedCv);
assertEquals(CallbackState.LOST, validatedCallback.getLastCallback());
}
// @Override // @Override
// public void tearDown() throws Exception { // public void tearDown() throws Exception {
// super.tearDown(); // super.tearDown();