diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index f12bfc33ea..181668122f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -25,8 +25,8 @@ import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; @@ -2605,21 +2605,12 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; - final boolean partialConnectivity = - (msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY) - || (nai.networkMisc.acceptPartialConnectivity - && nai.partialConnectivity); - // Once a network is determined to have partial connectivity, it cannot - // go back to full connectivity without a disconnect. This is because - // NetworkMonitor can only communicate either PARTIAL_CONNECTIVITY or VALID, - // but not both. - // TODO: Provide multi-testResult to improve the communication between - // ConnectivityService and NetworkMonitor, so that ConnectivityService could - // know the real status of network. + final boolean wasPartial = nai.partialConnectivity; + nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0); final boolean partialConnectivityChanged = - (partialConnectivity && !nai.partialConnectivity); + (wasPartial != nai.partialConnectivity); - final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID); + final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified @@ -2649,21 +2640,23 @@ public class ConnectivityService extends IConnectivityManager.Stub if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); if (valid) { handleFreshlyValidatedNetwork(nai); - // Clear NO_INTERNET and LOST_INTERNET notifications if network becomes - // valid. + // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET + // notifications if network becomes valid. mNotifier.clearNotification(nai.network.netId, NotificationType.NO_INTERNET); mNotifier.clearNotification(nai.network.netId, NotificationType.LOST_INTERNET); + mNotifier.clearNotification(nai.network.netId, + NotificationType.PARTIAL_CONNECTIVITY); } } else if (partialConnectivityChanged) { - nai.partialConnectivity = partialConnectivity; updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); } updateInetCondition(nai); // Let the NetworkAgent know the state of its network Bundle redirectUrlBundle = new Bundle(); redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl); + // TODO: Evaluate to update partial connectivity to status to NetworkAgent. nai.asyncChannel.sendMessage( NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), @@ -3443,6 +3436,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // Inform NetworkMonitor that partial connectivity is acceptable. This will likely // result in a partial connectivity result which will be processed by // maybeHandleNetworkMonitorMessage. + // + // TODO: NetworkMonitor does not refer to the "never ask again" bit. The bit is stored + // per network. Therefore, NetworkMonitor may still do https probe. try { nai.networkMonitor().setAcceptPartialConnectivity(); } catch (RemoteException e) { diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index fa059fa9e8..6560f58cde 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -30,9 +30,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; @@ -443,6 +446,16 @@ public class ConnectivityServiceTest { } private class MockNetworkAgent { + private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTP + | NETWORK_VALIDATION_PROBE_HTTPS; + private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE + | NETWORK_VALIDATION_RESULT_VALID; + private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE + | NETWORK_VALIDATION_PROBE_FALLBACK + | NETWORK_VALIDATION_RESULT_PARTIAL; + private static final int VALIDATION_RESULT_INVALID = 0; + private final INetworkMonitor mNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; @@ -460,17 +473,17 @@ public class ConnectivityServiceTest { private String mRedirectUrl; private INetworkMonitorCallbacks mNmCallbacks; - private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + private int mNmValidationResult = VALIDATION_RESULT_BASE; private String mNmValidationRedirectUrl = null; private boolean mNmProvNotificationRequested = false; void setNetworkValid() { - mNmValidationResult = NETWORK_TEST_RESULT_VALID; + mNmValidationResult = VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; } void setNetworkInvalid() { - mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + mNmValidationResult = VALIDATION_RESULT_INVALID; mNmValidationRedirectUrl = null; } @@ -480,7 +493,12 @@ public class ConnectivityServiceTest { } void setNetworkPartial() { - mNmValidationResult = NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; + mNmValidationResult = VALIDATION_RESULT_PARTIAL; + mNmValidationRedirectUrl = null; + } + + void setNetworkPartialValid() { + mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; } @@ -597,7 +615,7 @@ public class ConnectivityServiceTest { private void onValidationRequested() { try { if (mNmProvNotificationRequested - && mNmValidationResult == NETWORK_TEST_RESULT_VALID) { + && mNmValidationResult == VALIDATION_RESULT_VALID) { mNmCallbacks.hideProvisioningNotification(); mNmProvNotificationRequested = false; } @@ -2651,7 +2669,7 @@ public class ConnectivityServiceTest { // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http // probe. - mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.setNetworkPartialValid(); // If the user chooses yes to use this partial connectivity wifi, switch the default // network to wifi and check if wifi becomes valid or not. mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, @@ -2748,6 +2766,54 @@ public class ConnectivityServiceTest { callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); } + @Test + public void testCaptivePortalOnPartialConnectivity() throws RemoteException { + 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); + + // Bring up a network with a captive portal. + // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + String firstRedirectUrl = "http://example.com/firstPath"; + mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); + + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. + mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork()); + verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) + .launchCaptivePortalApp(); + + // Report that the captive portal is dismissed with partial connectivity, and check that + // callbacks are fired. + mWiFiNetworkAgent.setNetworkPartial(); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + // Report partial connectivity is accepted. + mWiFiNetworkAgent.setNetworkPartialValid(); + mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, + false /* always */); + waitForIdle(); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + NetworkCapabilities nc = + validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(captivePortalCallback); + mCm.unregisterNetworkCallback(validatedCallback); + } + @Test public void testCaptivePortal() { final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();