From 553969769d6883acbef78186fbc50f416e349adf Mon Sep 17 00:00:00 2001 From: Hansen Kurli Date: Fri, 28 Oct 2022 03:31:17 +0000 Subject: [PATCH] Disconnect network when captive portal returns unwanted. When the user selects "Do not use this network" in the captive portal app, it indicates that the user does not want to log in and does not want to use the network. Thus, the network should be disconnected. Internally handle the network disconnect in ConnectivityService upon receiving a captive portal app response of APP_RETURN_UNWANTED. Test: atest ConnectivityServiceTest Change-Id: I789ddb663b533a71a77ed435fa91b0c20d3bbfd4 --- .../android/server/ConnectivityService.java | 16 +++++ .../server/ConnectivityServiceTest.java | 70 ++++++++++++++----- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 2535974c19..dd4f33bfa5 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -736,6 +736,12 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_INITIAL_EVALUATION_TIMEOUT = 57; + /** + * Used internally when the user does not want the network from captive portal app. + * obj = Network + */ + private static final int EVENT_USER_DOES_NOT_WANT = 58; + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. @@ -5055,6 +5061,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public void appResponse(final int response) { if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) { enforceSettingsPermission(); + } else if (response == CaptivePortal.APP_RETURN_UNWANTED) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_USER_DOES_NOT_WANT, mNetwork)); + // Since the network will be disconnected, skip notifying NetworkMonitor + return; } final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork); @@ -5496,6 +5506,12 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_INGRESS_RATE_LIMIT_CHANGED: handleIngressRateLimitChanged(); break; + case EVENT_USER_DOES_NOT_WANT: + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj); + if (nai == null) break; + nai.onPreventAutomaticReconnect(); + nai.disconnect(); + break; } } } diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 8c9917ed52..3d8073f39c 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -228,6 +228,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.res.Resources; import android.location.LocationManager; +import android.net.CaptivePortal; import android.net.CaptivePortalData; import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager.DataStallReport; @@ -4440,6 +4441,27 @@ public class ConnectivityServiceTest { validatedCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent); } + private Intent startCaptivePortalApp(TestNetworkAgentWrapper networkAgent) throws Exception { + Network network = networkAgent.getNetwork(); + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. + mCm.startCaptivePortalApp(network); + waitForIdle(); + verify(networkAgent.mNetworkMonitor).launchCaptivePortalApp(); + + // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal) + final Bundle testBundle = new Bundle(); + final String testKey = "testkey"; + final String testValue = "testvalue"; + testBundle.putString(testKey, testValue); + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_GRANTED); + mCm.startCaptivePortalApp(network, testBundle); + final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); + assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); + assertEquals(testValue, signInIntent.getStringExtra(testKey)); + return signInIntent; + } + @Test public void testCaptivePortalApp() throws Exception { final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); @@ -4476,22 +4498,7 @@ public class ConnectivityServiceTest { captivePortalCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent); - // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. - mCm.startCaptivePortalApp(wifiNetwork); - waitForIdle(); - verify(mWiFiNetworkAgent.mNetworkMonitor).launchCaptivePortalApp(); - - // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal) - final Bundle testBundle = new Bundle(); - final String testKey = "testkey"; - final String testValue = "testvalue"; - testBundle.putString(testKey, testValue); - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_GRANTED); - mCm.startCaptivePortalApp(wifiNetwork, testBundle); - final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); - assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); - assertEquals(testValue, signInIntent.getStringExtra(testKey)); + startCaptivePortalApp(mWiFiNetworkAgent); // Report that the captive portal is dismissed, and check that callbacks are fired mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); @@ -4503,6 +4510,37 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(captivePortalCallback); } + @Test + public void testCaptivePortalApp_IgnoreNetwork() throws Exception { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + final Intent signInIntent = startCaptivePortalApp(mWiFiNetworkAgent); + final CaptivePortal captivePortal = signInIntent + .getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); + + captivePortal.ignoreNetwork(); + waitForIdle(); + + // Since network will disconnect, ensure no notification of response to NetworkMonitor + verify(mWiFiNetworkAgent.mNetworkMonitor, never()) + .notifyCaptivePortalAppFinished(CaptivePortal.APP_RETURN_UNWANTED); + + // Report that the network is disconnected + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent.expectPreventReconnectReceived(); + verify(mWiFiNetworkAgent.mNetworkMonitor).notifyNetworkDisconnected(); + captivePortalCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(captivePortalCallback); + } + @Test public void testAvoidOrIgnoreCaptivePortals() throws Exception { final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();