Loading core/java/android/provider/Settings.java +34 −0 Original line number Diff line number Diff line Loading @@ -8061,12 +8061,46 @@ public final class Settings { */ public static final String PAC_CHANGE_DELAY = "pac_change_delay"; /** * Don't attempt to detect captive portals. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; /** * When detecting a captive portal, display a notification that * prompts the user to sign in. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; /** * When detecting a captive portal, immediately disconnect from the * network and do not reconnect to that network in the future. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; /** * What to do when connecting a network that presents a captive portal. * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. * * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. * @hide */ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; /** * Setting to turn off captive portal detection. Feature is enabled by * default and the setting needs to be set to 0 to disable it. * * @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection * @hide */ @Deprecated public static final String CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled"; Loading services/core/java/com/android/server/ConnectivityService.java +15 −1 Original line number Diff line number Diff line Loading @@ -2255,11 +2255,19 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mNetworkForNetId) { nai = mNetworkForNetId.get(netId); } // If captive portal status has changed, update capabilities. // If captive portal status has changed, update capabilities or disconnect. if (nai != null && (visible != nai.lastCaptivePortalDetected)) { final int oldScore = nai.getCurrentScore(); nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; if (nai.lastCaptivePortalDetected && Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) { if (DBG) log("Avoiding captive portal network: " + nai.name()); nai.asyncChannel.sendMessage( NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT); teardownUnneededNetwork(nai); break; } updateCapabilities(oldScore, nai, nai.networkCapabilities); } if (!visible) { Loading @@ -2280,6 +2288,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } private int getCaptivePortalMode() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); } private boolean maybeHandleNetworkAgentInfoMessage(Message msg) { switch (msg.what) { default: Loading services/core/java/com/android/server/connectivity/NetworkMonitor.java +9 −3 Original line number Diff line number Diff line Loading @@ -211,7 +211,9 @@ public class NetworkMonitor extends StateMachine { private final NetworkRequest mDefaultRequest; private final IpConnectivityLog mMetricsLog; private boolean mIsCaptivePortalCheckEnabled; @VisibleForTesting protected boolean mIsCaptivePortalCheckEnabled; private boolean mUseHttps; // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. Loading Loading @@ -265,7 +267,8 @@ public class NetworkMonitor extends StateMachine { setInitialState(mDefaultState); mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT) != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE; mUseHttps = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; Loading Loading @@ -632,7 +635,10 @@ public class NetworkMonitor extends StateMachine { @VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204); if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return new CaptivePortalProbeResult(204); } URL pacUrl = null, httpsUrl = null, httpUrl = null, fallbackUrl = null; Loading services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +60 −5 Original line number Diff line number Diff line Loading @@ -236,6 +236,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private final IdleableHandlerThread mHandlerThread; private final ConditionVariable mDisconnected = new ConditionVariable(); private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); private int mScore; private NetworkAgent mNetworkAgent; private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED; Loading Loading @@ -291,6 +292,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mRedirectUrl = redirectUrl; mNetworkStatusReceived.open(); } @Override protected void preventAutomaticReconnect() { mPreventReconnectReceived.open(); } }; // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. Loading Loading @@ -375,11 +381,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWrappedNetworkMonitor.gen204ProbeResult = 200; mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl; 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; mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null; } public void disconnect() { Loading @@ -391,6 +392,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { return new Network(mNetworkAgent.netId); } public ConditionVariable getPreventReconnectReceived() { return mPreventReconnectReceived; } public ConditionVariable getDisconnectedCV() { return mDisconnected; } Loading Loading @@ -597,6 +602,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { @Override protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); } return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null); } } Loading Loading @@ -743,6 +749,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { mService.systemReady(); mCm = new WrappedConnectivityManager(getContext(), mService); mCm.bindProcessToNetwork(null); // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); } public void tearDown() throws Exception { Loading Loading @@ -1710,6 +1719,47 @@ public class ConnectivityServiceTest extends AndroidTestCase { validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); } @LargeTest public void testAvoidOrIgnoreCaptivePortals() { 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); setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID); // Bring up a network with a captive portal. // Expect it to fail to connect and not result in any callbacks. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV(); ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived(); mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); waitFor(disconnectCv); waitFor(avoidCv); assertNoCallbacks(captivePortalCallback, validatedCallback); // Now test ignore mode. setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); // Bring up a network with a captive portal. // Since we're ignoring captive portals, the network will validate. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); // But there should be no CaptivePortal callback. captivePortalCallback.assertNoCallback(); } @SmallTest public void testInvalidNetworkSpecifier() { boolean execptionCalled = true; Loading Loading @@ -1850,6 +1900,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCm.unregisterNetworkCallback(cellNetworkCallback); } private void setCaptivePortalMode(int mode) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); } private void setMobileDataAlwaysOn(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); Loading Loading
core/java/android/provider/Settings.java +34 −0 Original line number Diff line number Diff line Loading @@ -8061,12 +8061,46 @@ public final class Settings { */ public static final String PAC_CHANGE_DELAY = "pac_change_delay"; /** * Don't attempt to detect captive portals. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; /** * When detecting a captive portal, display a notification that * prompts the user to sign in. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; /** * When detecting a captive portal, immediately disconnect from the * network and do not reconnect to that network in the future. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; /** * What to do when connecting a network that presents a captive portal. * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. * * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. * @hide */ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; /** * Setting to turn off captive portal detection. Feature is enabled by * default and the setting needs to be set to 0 to disable it. * * @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection * @hide */ @Deprecated public static final String CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled"; Loading
services/core/java/com/android/server/ConnectivityService.java +15 −1 Original line number Diff line number Diff line Loading @@ -2255,11 +2255,19 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mNetworkForNetId) { nai = mNetworkForNetId.get(netId); } // If captive portal status has changed, update capabilities. // If captive portal status has changed, update capabilities or disconnect. if (nai != null && (visible != nai.lastCaptivePortalDetected)) { final int oldScore = nai.getCurrentScore(); nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; if (nai.lastCaptivePortalDetected && Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) { if (DBG) log("Avoiding captive portal network: " + nai.name()); nai.asyncChannel.sendMessage( NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT); teardownUnneededNetwork(nai); break; } updateCapabilities(oldScore, nai, nai.networkCapabilities); } if (!visible) { Loading @@ -2280,6 +2288,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } private int getCaptivePortalMode() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); } private boolean maybeHandleNetworkAgentInfoMessage(Message msg) { switch (msg.what) { default: Loading
services/core/java/com/android/server/connectivity/NetworkMonitor.java +9 −3 Original line number Diff line number Diff line Loading @@ -211,7 +211,9 @@ public class NetworkMonitor extends StateMachine { private final NetworkRequest mDefaultRequest; private final IpConnectivityLog mMetricsLog; private boolean mIsCaptivePortalCheckEnabled; @VisibleForTesting protected boolean mIsCaptivePortalCheckEnabled; private boolean mUseHttps; // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. Loading Loading @@ -265,7 +267,8 @@ public class NetworkMonitor extends StateMachine { setInitialState(mDefaultState); mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT) != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE; mUseHttps = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; Loading Loading @@ -632,7 +635,10 @@ public class NetworkMonitor extends StateMachine { @VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204); if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return new CaptivePortalProbeResult(204); } URL pacUrl = null, httpsUrl = null, httpUrl = null, fallbackUrl = null; Loading
services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +60 −5 Original line number Diff line number Diff line Loading @@ -236,6 +236,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private final IdleableHandlerThread mHandlerThread; private final ConditionVariable mDisconnected = new ConditionVariable(); private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); private int mScore; private NetworkAgent mNetworkAgent; private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED; Loading Loading @@ -291,6 +292,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mRedirectUrl = redirectUrl; mNetworkStatusReceived.open(); } @Override protected void preventAutomaticReconnect() { mPreventReconnectReceived.open(); } }; // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. Loading Loading @@ -375,11 +381,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWrappedNetworkMonitor.gen204ProbeResult = 200; mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl; 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; mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null; } public void disconnect() { Loading @@ -391,6 +392,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { return new Network(mNetworkAgent.netId); } public ConditionVariable getPreventReconnectReceived() { return mPreventReconnectReceived; } public ConditionVariable getDisconnectedCV() { return mDisconnected; } Loading Loading @@ -597,6 +602,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { @Override protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); } return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null); } } Loading Loading @@ -743,6 +749,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { mService.systemReady(); mCm = new WrappedConnectivityManager(getContext(), mService); mCm.bindProcessToNetwork(null); // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); } public void tearDown() throws Exception { Loading Loading @@ -1710,6 +1719,47 @@ public class ConnectivityServiceTest extends AndroidTestCase { validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); } @LargeTest public void testAvoidOrIgnoreCaptivePortals() { 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); setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID); // Bring up a network with a captive portal. // Expect it to fail to connect and not result in any callbacks. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV(); ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived(); mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); waitFor(disconnectCv); waitFor(avoidCv); assertNoCallbacks(captivePortalCallback, validatedCallback); // Now test ignore mode. setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); // Bring up a network with a captive portal. // Since we're ignoring captive portals, the network will validate. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); // But there should be no CaptivePortal callback. captivePortalCallback.assertNoCallback(); } @SmallTest public void testInvalidNetworkSpecifier() { boolean execptionCalled = true; Loading Loading @@ -1850,6 +1900,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCm.unregisterNetworkCallback(cellNetworkCallback); } private void setCaptivePortalMode(int mode) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); } private void setMobileDataAlwaysOn(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); Loading