Loading core/java/android/net/NetworkCapabilities.java +1 −0 Original line number Diff line number Diff line Loading @@ -792,6 +792,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break; case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break; case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break; case NET_CAPABILITY_CAPTIVE_PORTAL: capabilities += "CAPTIVE_PORTAL"; break; } if (++i < types.length) capabilities += "&"; } Loading services/core/java/com/android/server/ConnectivityService.java +78 −35 Original line number Diff line number Diff line Loading @@ -2171,6 +2171,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultInetConditionPublished = 0; } 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); mKeepaliveTracker.handleStopAllKeepalives(nai, ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK); Loading Loading @@ -2288,7 +2292,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // is currently satisfying the request. This is desirable when // cellular ends up validating but WiFi does not. // 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. mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < nai.getCurrentScoreAsValidated())) { Loading Loading @@ -3866,10 +3870,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network // 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( linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest); mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this); synchronized (this) { nai.networkMonitor.systemReady = mSystemReady; } Loading Loading @@ -4265,8 +4269,9 @@ public class ConnectivityService extends IConnectivityManager.Stub ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>(); if (VDBG) log(" network has: " + newNetwork.networkCapabilities); for (NetworkRequestInfo nri : mNetworkRequests.values()) { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); if (newNetwork == currentNetwork) { final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); final boolean satisfies = newNetwork.satisfies(nri.request); if (newNetwork == currentNetwork && satisfies) { if (VDBG) { log("Network " + newNetwork.name() + " was already satisfying" + " request " + nri.request.requestId + ". No change."); Loading @@ -4277,7 +4282,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // check if it satisfies the NetworkCapabilities if (VDBG) log(" checking if request is satisfied: " + nri.request); if (newNetwork.satisfies(nri.request)) { if (satisfies) { if (!nri.isRequest) { // This is not a request, it's a callback listener. // Add it to newNetwork regardless of score. Loading Loading @@ -4320,6 +4325,37 @@ public class ConnectivityService extends IConnectivityManager.Stub 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. Loading @@ -4338,7 +4374,6 @@ public class ConnectivityService extends IConnectivityManager.Stub unlinger(nai); } } if (keep) { if (isNewDefault) { // Notify system services that this network is up. makeDefault(newNetwork); Loading Loading @@ -4373,6 +4408,7 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyLockdownVpn(newNetwork); } if (keep) { // Notify battery stats service about this network, both the normal // interface and any stacked links. // TODO: Avoid redoing this; this must only be done once when a network comes online. Loading Loading @@ -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); } } services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +4 −3 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.Messenger; import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.server.ConnectivityService; import com.android.server.connectivity.NetworkMonitor; import java.util.ArrayList; Loading @@ -51,7 +52,7 @@ import java.util.Comparator; // ConnectivityService will tell netd to create the network and immediately transition to // state #3. // 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. // If this network cannot satisfy the default NetworkRequest, it will immediately be // transitioned to state #4. Loading Loading @@ -164,7 +165,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc, NetworkRequest defaultRequest) { NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) { this.messenger = messenger; asyncChannel = ac; network = net; Loading @@ -172,7 +173,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { linkProperties = lp; networkCapabilities = nc; currentScore = score; networkMonitor = new NetworkMonitor(context, handler, this, defaultRequest); networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest); networkMisc = misc; } Loading services/core/java/com/android/server/connectivity/NetworkMonitor.java +4 −3 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ public class NetworkMonitor extends StateMachine { // Being in the ValidatedState State indicates a Network is: // - Successfully validated, 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 { @Override public void enter() { Loading Loading @@ -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 // 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 // allows apps to wrap up communication and allows for seamless reactivation if the other // higher scoring Network happens to disconnect. Loading Loading @@ -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. * Returns HTTP response code. */ private int isCaptivePortal() { @VisibleForTesting protected int isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) return 204; HttpURLConnection urlConnection = null; Loading services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +139 −41 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import android.util.LogPrinter; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import org.mockito.ArgumentCaptor; Loading Loading @@ -118,7 +119,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private INetworkPolicyManager mPolicyService; private BroadcastInterceptingContext mServiceContext; private ConnectivityService mService; private WrappedConnectivityService mService; private ConnectivityManager mCm; private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mCellNetworkAgent; Loading Loading @@ -148,6 +149,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { } private class MockNetworkAgent { private final WrappedNetworkMonitor mWrappedNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; private final Thread mThread; Loading @@ -172,6 +174,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { throw new UnsupportedOperationException("unimplemented network type"); } final ConditionVariable initComplete = new ConditionVariable(); final ConditionVariable networkMonitorAvailable = mService.getNetworkMonitorCreatedCV(); mThread = new Thread() { public void run() { Looper.prepare(); Loading @@ -186,6 +189,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { }; mThread.start(); waitFor(initComplete); waitFor(networkMonitorAvailable); mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor(); } public void adjustScore(int change) { Loading @@ -211,44 +216,46 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE); 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; final ConditionVariable validatedCv = new ConditionVariable(); if (validated) { // If we connect a network without INTERNET capability, it'll get reaped. // Prevent the reaping by adding a NetworkRequest. mWrappedNetworkMonitor.gen204ProbeResult = 204; NetworkRequest request = new NetworkRequest.Builder() .addTransportType(mNetworkCapabilities.getTransportTypes()[0]) .build(); callback = new NetworkCallback() { public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { if (network.equals(getNetwork()) && networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { validatedCv.open(); } } }; mCm.requestNetwork(request, callback); } else { mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mCm.registerNetworkCallback(request, callback); } addCapability(NET_CAPABILITY_INTERNET); connectWithoutInternet(); if (validated) { // Wait for network to validate. waitFor(validatedCv); mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mWrappedNetworkMonitor.gen204ProbeResult = 500; } 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() { mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); mNetworkAgent.sendNetworkInfo(mNetworkInfo); Loading @@ -261,14 +268,18 @@ public class ConnectivityServiceTest extends AndroidTestCase { public ConditionVariable getDisconnectedCV() { return mDisconnected; } public WrappedNetworkMonitor getWrappedNetworkMonitor() { return mWrappedNetworkMonitor; } } private static class MockNetworkFactory extends NetworkFactory { final ConditionVariable mNetworkStartedCV = new ConditionVariable(); final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); private final ConditionVariable mNetworkStartedCV = new ConditionVariable(); private final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); private final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); private final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { Loading Loading @@ -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 final ConditionVariable mNetworkMonitorCreated = new ConditionVariable(); private WrappedNetworkMonitor mLastCreatedNetworkMonitor; public WrappedConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager) { super(context, netManager, statsService, policyManager); Loading Loading @@ -360,6 +390,25 @@ public class ConnectivityServiceTest extends AndroidTestCase { 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 { Loading Loading @@ -586,29 +635,29 @@ public class ConnectivityServiceTest extends AndroidTestCase { @LargeTest public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated cellular. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(false); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test bringing up validated WiFi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); // Test bringing up validated cellular. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.connect(true); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test WiFi disconnect. // Test cellular disconnect. cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.disconnect(); mCellNetworkAgent.disconnect(); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); verifyActiveNetwork(TRANSPORT_WIFI); // 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)); } Loading Loading @@ -846,12 +895,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { cellCv = cellNetworkCallback.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.connect(true); waitFor(cv); waitFor(cellCv); assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback()); assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); Loading @@ -868,12 +913,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { cellCv = cellNetworkCallback.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.connect(true); waitFor(cv); waitFor(wifiCv); assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback()); waitFor(cellCv); Loading Loading @@ -1075,6 +1116,63 @@ public class ConnectivityServiceTest extends AndroidTestCase { 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 // public void tearDown() throws Exception { // super.tearDown(); Loading Loading
core/java/android/net/NetworkCapabilities.java +1 −0 Original line number Diff line number Diff line Loading @@ -792,6 +792,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break; case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break; case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break; case NET_CAPABILITY_CAPTIVE_PORTAL: capabilities += "CAPTIVE_PORTAL"; break; } if (++i < types.length) capabilities += "&"; } Loading
services/core/java/com/android/server/ConnectivityService.java +78 −35 Original line number Diff line number Diff line Loading @@ -2171,6 +2171,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultInetConditionPublished = 0; } 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); mKeepaliveTracker.handleStopAllKeepalives(nai, ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK); Loading Loading @@ -2288,7 +2292,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // is currently satisfying the request. This is desirable when // cellular ends up validating but WiFi does not. // 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. mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < nai.getCurrentScoreAsValidated())) { Loading Loading @@ -3866,10 +3870,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network // 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( linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest); mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this); synchronized (this) { nai.networkMonitor.systemReady = mSystemReady; } Loading Loading @@ -4265,8 +4269,9 @@ public class ConnectivityService extends IConnectivityManager.Stub ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>(); if (VDBG) log(" network has: " + newNetwork.networkCapabilities); for (NetworkRequestInfo nri : mNetworkRequests.values()) { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); if (newNetwork == currentNetwork) { final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); final boolean satisfies = newNetwork.satisfies(nri.request); if (newNetwork == currentNetwork && satisfies) { if (VDBG) { log("Network " + newNetwork.name() + " was already satisfying" + " request " + nri.request.requestId + ". No change."); Loading @@ -4277,7 +4282,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // check if it satisfies the NetworkCapabilities if (VDBG) log(" checking if request is satisfied: " + nri.request); if (newNetwork.satisfies(nri.request)) { if (satisfies) { if (!nri.isRequest) { // This is not a request, it's a callback listener. // Add it to newNetwork regardless of score. Loading Loading @@ -4320,6 +4325,37 @@ public class ConnectivityService extends IConnectivityManager.Stub 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. Loading @@ -4338,7 +4374,6 @@ public class ConnectivityService extends IConnectivityManager.Stub unlinger(nai); } } if (keep) { if (isNewDefault) { // Notify system services that this network is up. makeDefault(newNetwork); Loading Loading @@ -4373,6 +4408,7 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyLockdownVpn(newNetwork); } if (keep) { // Notify battery stats service about this network, both the normal // interface and any stacked links. // TODO: Avoid redoing this; this must only be done once when a network comes online. Loading Loading @@ -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); } }
services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +4 −3 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.Messenger; import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.server.ConnectivityService; import com.android.server.connectivity.NetworkMonitor; import java.util.ArrayList; Loading @@ -51,7 +52,7 @@ import java.util.Comparator; // ConnectivityService will tell netd to create the network and immediately transition to // state #3. // 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. // If this network cannot satisfy the default NetworkRequest, it will immediately be // transitioned to state #4. Loading Loading @@ -164,7 +165,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc, NetworkRequest defaultRequest) { NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) { this.messenger = messenger; asyncChannel = ac; network = net; Loading @@ -172,7 +173,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { linkProperties = lp; networkCapabilities = nc; currentScore = score; networkMonitor = new NetworkMonitor(context, handler, this, defaultRequest); networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest); networkMisc = misc; } Loading
services/core/java/com/android/server/connectivity/NetworkMonitor.java +4 −3 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ public class NetworkMonitor extends StateMachine { // Being in the ValidatedState State indicates a Network is: // - Successfully validated, 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 { @Override public void enter() { Loading Loading @@ -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 // 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 // allows apps to wrap up communication and allows for seamless reactivation if the other // higher scoring Network happens to disconnect. Loading Loading @@ -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. * Returns HTTP response code. */ private int isCaptivePortal() { @VisibleForTesting protected int isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) return 204; HttpURLConnection urlConnection = null; Loading
services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +139 −41 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import android.util.LogPrinter; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import org.mockito.ArgumentCaptor; Loading Loading @@ -118,7 +119,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private INetworkPolicyManager mPolicyService; private BroadcastInterceptingContext mServiceContext; private ConnectivityService mService; private WrappedConnectivityService mService; private ConnectivityManager mCm; private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mCellNetworkAgent; Loading Loading @@ -148,6 +149,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { } private class MockNetworkAgent { private final WrappedNetworkMonitor mWrappedNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; private final Thread mThread; Loading @@ -172,6 +174,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { throw new UnsupportedOperationException("unimplemented network type"); } final ConditionVariable initComplete = new ConditionVariable(); final ConditionVariable networkMonitorAvailable = mService.getNetworkMonitorCreatedCV(); mThread = new Thread() { public void run() { Looper.prepare(); Loading @@ -186,6 +189,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { }; mThread.start(); waitFor(initComplete); waitFor(networkMonitorAvailable); mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor(); } public void adjustScore(int change) { Loading @@ -211,44 +216,46 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE); 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; final ConditionVariable validatedCv = new ConditionVariable(); if (validated) { // If we connect a network without INTERNET capability, it'll get reaped. // Prevent the reaping by adding a NetworkRequest. mWrappedNetworkMonitor.gen204ProbeResult = 204; NetworkRequest request = new NetworkRequest.Builder() .addTransportType(mNetworkCapabilities.getTransportTypes()[0]) .build(); callback = new NetworkCallback() { public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { if (network.equals(getNetwork()) && networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { validatedCv.open(); } } }; mCm.requestNetwork(request, callback); } else { mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mCm.registerNetworkCallback(request, callback); } addCapability(NET_CAPABILITY_INTERNET); connectWithoutInternet(); if (validated) { // Wait for network to validate. waitFor(validatedCv); mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mWrappedNetworkMonitor.gen204ProbeResult = 500; } 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() { mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); mNetworkAgent.sendNetworkInfo(mNetworkInfo); Loading @@ -261,14 +268,18 @@ public class ConnectivityServiceTest extends AndroidTestCase { public ConditionVariable getDisconnectedCV() { return mDisconnected; } public WrappedNetworkMonitor getWrappedNetworkMonitor() { return mWrappedNetworkMonitor; } } private static class MockNetworkFactory extends NetworkFactory { final ConditionVariable mNetworkStartedCV = new ConditionVariable(); final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); private final ConditionVariable mNetworkStartedCV = new ConditionVariable(); private final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); private final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); private final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { Loading Loading @@ -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 final ConditionVariable mNetworkMonitorCreated = new ConditionVariable(); private WrappedNetworkMonitor mLastCreatedNetworkMonitor; public WrappedConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager) { super(context, netManager, statsService, policyManager); Loading Loading @@ -360,6 +390,25 @@ public class ConnectivityServiceTest extends AndroidTestCase { 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 { Loading Loading @@ -586,29 +635,29 @@ public class ConnectivityServiceTest extends AndroidTestCase { @LargeTest public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated cellular. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(false); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test bringing up validated WiFi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); // Test bringing up validated cellular. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.connect(true); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test WiFi disconnect. // Test cellular disconnect. cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.disconnect(); mCellNetworkAgent.disconnect(); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); verifyActiveNetwork(TRANSPORT_WIFI); // 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)); } Loading Loading @@ -846,12 +895,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { cellCv = cellNetworkCallback.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.connect(true); waitFor(cv); waitFor(cellCv); assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback()); assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); Loading @@ -868,12 +913,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { cellCv = cellNetworkCallback.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.connect(true); waitFor(cv); waitFor(wifiCv); assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback()); waitFor(cellCv); Loading Loading @@ -1075,6 +1116,63 @@ public class ConnectivityServiceTest extends AndroidTestCase { 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 // public void tearDown() throws Exception { // super.tearDown(); Loading