Loading src/android/net/dhcp/DhcpClient.java +3 −3 Original line number Diff line number Diff line Loading @@ -503,9 +503,9 @@ public class DhcpClient extends StateMachine { * check whether or not to support caching the last lease info and INIT-REBOOT state. * * INIT-REBOOT state is supported on Android R by default if there is no experiment flag set to * disable this feature explicitly, meanwhile we still hope to be able to control this feature * on/off by pushing experiment flag for A/B testing and metrics collection on both of Android * Q and R version, however it's disbled on Android Q by default. * disable this feature explicitly, meanwhile turning this feature on/off by pushing experiment * flag makes it possible to do A/B test and metrics collection on both of Android Q and R, but * it's disabled on Android Q by default. */ public boolean isDhcpLeaseCacheEnabled() { final boolean defaultEnabled = Loading src/android/net/ip/IpClient.java +22 −4 Original line number Diff line number Diff line Loading @@ -482,6 +482,7 @@ public class IpClient extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; private MacAddress mCurrentBssid; private boolean mHasDisabledIPv6OnProvLoss; /** * Reading the snapshot is an asynchronous operation initiated by invoking Loading Loading @@ -1137,9 +1138,9 @@ public class IpClient extends StateMachine { // Note that we can still be disconnected by IpReachabilityMonitor // if the IPv6 default gateway (but not the IPv6 DNS servers; see // accompanying code in IpReachabilityMonitor) is unreachable. final boolean ignoreIPv6ProvisioningLoss = mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker && !mCm.shouldAvoidBadWifi(); final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIPv6OnProvLoss || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker && !mCm.shouldAvoidBadWifi()); // Additionally: // Loading @@ -1163,8 +1164,24 @@ public class IpClient extends StateMachine { // IPv6 default route then also consider the loss of that default route // to be a loss of provisioning. See b/27962810. if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { // Although link properties have lost IPv6 default route in this case, if IPv4 is still // working with appropriate routes and DNS servers, we can keep the current connection // without disconnecting from the network, just disable IPv6 on that given network until // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be // stripped out, so applications will be able to reconnect immediately over IPv4. See // b/131781810. if (newLp.isIpv4Provisioned()) { mInterfaceCtrl.disableIPv6(); mHasDisabledIPv6OnProvLoss = true; delta = PROV_CHANGE_STILL_PROVISIONED; if (DBG) { mLog.log("Disable IPv6 stack completely when the default router has gone"); } } else { delta = PROV_CHANGE_LOST_PROVISIONING; } } return delta; } Loading Loading @@ -1591,6 +1608,7 @@ public class IpClient extends StateMachine { @Override public void enter() { stopAllIP(); mHasDisabledIPv6OnProvLoss = false; mLinkObserver.clearInterfaceParams(); resetLinkProperties(); Loading src/com/android/server/connectivity/NetworkMonitor.java +5 −0 Original line number Diff line number Diff line Loading @@ -793,6 +793,11 @@ public class NetworkMonitor extends StateMachine { return HANDLED; case CMD_FORCE_REEVALUATION: case CMD_CAPTIVE_PORTAL_RECHECK: if (getCurrentState() == mDefaultState) { // Before receiving CMD_NETWORK_CONNECTED (when still in mDefaultState), // requests to reevaluate are not valid: drop them. return HANDLED; } String msg = "Forcing reevaluation for UID " + message.arg1; final DnsStallDetector dsd = getDnsStallDetector(); if (dsd != null) { Loading tests/integration/src/android/net/ip/IpClientIntegrationTest.java +88 −5 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; Loading Loading @@ -994,7 +995,7 @@ public class IpClientIntegrationTest { assertEquals(5, packetList.size()); assertArpProbe(packetList.get(0)); assertArpAnnounce(packetList.get(3)); } else { verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); Loading Loading @@ -1245,11 +1246,11 @@ public class IpClientIntegrationTest { fail("No router solicitation received on interface within timeout"); } private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception { final String dnsServer = "2001:4860:4860::64"; final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); ByteBuffer ra = buildRaPacket(pio, rdnss); ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss); if (waitForRs) { waitForRouterSolicitation(); Loading @@ -1258,6 +1259,14 @@ public class IpClientIntegrationTest { mPacketReader.sendResponse(ra); } private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { sendRouterAdvertisement(waitForRs, (short) 1800); } private void sendRouterAdvertisementWithZeroLifetime() throws Exception { sendRouterAdvertisement(false /* waitForRs */, (short) 0); } // TODO: move this and the following method to a common location and use them in ApfTest. private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString) throws Exception { Loading Loading @@ -1319,7 +1328,8 @@ public class IpClientIntegrationTest { return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6); } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options) throws Exception { final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01"); final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress(); Loading Loading @@ -1347,7 +1357,7 @@ public class IpClientIntegrationTest { packet.putShort((short) 0); // Checksum, TBD packet.put((byte) 0); // Hop limit, unspecified packet.put((byte) 0); // M=0, O=0 packet.putShort((short) 1800); // Router lifetime packet.putShort(lifetime); // Router lifetime packet.putInt(0); // Reachable time, unspecified packet.putInt(100); // Retrans time 100ms. Loading @@ -1367,6 +1377,10 @@ public class IpClientIntegrationTest { return packet; } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { return buildRaPacket((short) 1800, options); } private void disableIpv6ProvisioningDelays() throws Exception { // Speed up the test by disabling DAD and removing router_solicitation_delay. // We don't need to restore the default value because the interface is removed in tearDown. Loading Loading @@ -2153,4 +2167,73 @@ public class IpClientIntegrationTest { doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); } private void doDualStackProvisioning() throws Exception { when(mCm.shouldAvoidBadWifi()).thenReturn(true); final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .build(); // Accelerate DHCP handshake to shorten test duration, not strictly necessary. mDependencies.setDhcpRapidCommitEnabled(true); mIpc.startProvisioning(config); final InOrder inOrder = inOrder(mCb); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); final String dnsServer = "2001:4860:4860::64"; final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); final ByteBuffer ra = buildRaPacket(pio, rdnss); doIpv6OnlyProvisioning(inOrder, ra); // Start IPv4 provisioning and wait until entire provisioning completes. handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; lpFuture.complete(x); return true; })); final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(lp); assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); assertTrue(lp.getDnsServers().contains(SERVER_ADDR)); reset(mCb); } @Test public void testIgnoreIpv6ProvisioningLoss() throws Exception { doDualStackProvisioning(); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers // have been removed, then verify if there is IPv4-only info left in the LinkProperties. sendRouterAdvertisementWithZeroLifetime(); verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( argThat(x -> { final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1 && x.getDnsServers().size() == 1 && x.getAddresses().get(0) instanceof Inet4Address && x.getDnsServers().get(0) instanceof Inet4Address); if (!isOnlyIPv4Provisioned) return false; lpFuture.complete(x); return true; })); final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(lp); assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); } @Test public void testDualStackProvisioning() throws Exception { doDualStackProvisioning(); verify(mCb, never()).onProvisioningFailure(any()); } } tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; Loading Loading @@ -1229,6 +1230,32 @@ public class NetworkMonitorTest { verify(mCapportApiConnection, times(1)).getResponseCode(); } @Test public void testIsCaptivePortal_NoRevalidationBeforeNetworkConnected() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); final NetworkMonitor nm = makeCellMeteredNetworkMonitor(); final LinkProperties lp = makeCapportLPs(); // LinkProperties changed, but NM should not revalidate before notifyNetworkConnected nm.notifyLinkPropertiesChanged(lp); verify(mHttpConnection, after(100).never()).getResponseCode(); verify(mHttpsConnection, never()).getResponseCode(); verify(mCapportApiConnection, never()).getResponseCode(); setValidProbes(); setApiContent(mCapportApiConnection, "{'captive': true, " + "'user-portal-url': '" + TEST_LOGIN_URL + "'}"); // After notifyNetworkConnected, validation uses the capport API contents nm.notifyNetworkConnected(lp, CELL_METERED_CAPABILITIES); verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); verify(mHttpConnection, never()).getResponseCode(); verify(mCapportApiConnection).getResponseCode(); } @Test public void testIsCaptivePortal_CapportApiNotPortalNotValidated() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); Loading Loading
src/android/net/dhcp/DhcpClient.java +3 −3 Original line number Diff line number Diff line Loading @@ -503,9 +503,9 @@ public class DhcpClient extends StateMachine { * check whether or not to support caching the last lease info and INIT-REBOOT state. * * INIT-REBOOT state is supported on Android R by default if there is no experiment flag set to * disable this feature explicitly, meanwhile we still hope to be able to control this feature * on/off by pushing experiment flag for A/B testing and metrics collection on both of Android * Q and R version, however it's disbled on Android Q by default. * disable this feature explicitly, meanwhile turning this feature on/off by pushing experiment * flag makes it possible to do A/B test and metrics collection on both of Android Q and R, but * it's disabled on Android Q by default. */ public boolean isDhcpLeaseCacheEnabled() { final boolean defaultEnabled = Loading
src/android/net/ip/IpClient.java +22 −4 Original line number Diff line number Diff line Loading @@ -482,6 +482,7 @@ public class IpClient extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; private MacAddress mCurrentBssid; private boolean mHasDisabledIPv6OnProvLoss; /** * Reading the snapshot is an asynchronous operation initiated by invoking Loading Loading @@ -1137,9 +1138,9 @@ public class IpClient extends StateMachine { // Note that we can still be disconnected by IpReachabilityMonitor // if the IPv6 default gateway (but not the IPv6 DNS servers; see // accompanying code in IpReachabilityMonitor) is unreachable. final boolean ignoreIPv6ProvisioningLoss = mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker && !mCm.shouldAvoidBadWifi(); final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIPv6OnProvLoss || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker && !mCm.shouldAvoidBadWifi()); // Additionally: // Loading @@ -1163,8 +1164,24 @@ public class IpClient extends StateMachine { // IPv6 default route then also consider the loss of that default route // to be a loss of provisioning. See b/27962810. if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { // Although link properties have lost IPv6 default route in this case, if IPv4 is still // working with appropriate routes and DNS servers, we can keep the current connection // without disconnecting from the network, just disable IPv6 on that given network until // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be // stripped out, so applications will be able to reconnect immediately over IPv4. See // b/131781810. if (newLp.isIpv4Provisioned()) { mInterfaceCtrl.disableIPv6(); mHasDisabledIPv6OnProvLoss = true; delta = PROV_CHANGE_STILL_PROVISIONED; if (DBG) { mLog.log("Disable IPv6 stack completely when the default router has gone"); } } else { delta = PROV_CHANGE_LOST_PROVISIONING; } } return delta; } Loading Loading @@ -1591,6 +1608,7 @@ public class IpClient extends StateMachine { @Override public void enter() { stopAllIP(); mHasDisabledIPv6OnProvLoss = false; mLinkObserver.clearInterfaceParams(); resetLinkProperties(); Loading
src/com/android/server/connectivity/NetworkMonitor.java +5 −0 Original line number Diff line number Diff line Loading @@ -793,6 +793,11 @@ public class NetworkMonitor extends StateMachine { return HANDLED; case CMD_FORCE_REEVALUATION: case CMD_CAPTIVE_PORTAL_RECHECK: if (getCurrentState() == mDefaultState) { // Before receiving CMD_NETWORK_CONNECTED (when still in mDefaultState), // requests to reevaluate are not valid: drop them. return HANDLED; } String msg = "Forcing reevaluation for UID " + message.arg1; final DnsStallDetector dsd = getDnsStallDetector(); if (dsd != null) { Loading
tests/integration/src/android/net/ip/IpClientIntegrationTest.java +88 −5 Original line number Diff line number Diff line Loading @@ -171,6 +171,7 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; Loading Loading @@ -994,7 +995,7 @@ public class IpClientIntegrationTest { assertEquals(5, packetList.size()); assertArpProbe(packetList.get(0)); assertArpAnnounce(packetList.get(3)); } else { verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); Loading Loading @@ -1245,11 +1246,11 @@ public class IpClientIntegrationTest { fail("No router solicitation received on interface within timeout"); } private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception { final String dnsServer = "2001:4860:4860::64"; final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); ByteBuffer ra = buildRaPacket(pio, rdnss); ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss); if (waitForRs) { waitForRouterSolicitation(); Loading @@ -1258,6 +1259,14 @@ public class IpClientIntegrationTest { mPacketReader.sendResponse(ra); } private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { sendRouterAdvertisement(waitForRs, (short) 1800); } private void sendRouterAdvertisementWithZeroLifetime() throws Exception { sendRouterAdvertisement(false /* waitForRs */, (short) 0); } // TODO: move this and the following method to a common location and use them in ApfTest. private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString) throws Exception { Loading Loading @@ -1319,7 +1328,8 @@ public class IpClientIntegrationTest { return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6); } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options) throws Exception { final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01"); final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress(); Loading Loading @@ -1347,7 +1357,7 @@ public class IpClientIntegrationTest { packet.putShort((short) 0); // Checksum, TBD packet.put((byte) 0); // Hop limit, unspecified packet.put((byte) 0); // M=0, O=0 packet.putShort((short) 1800); // Router lifetime packet.putShort(lifetime); // Router lifetime packet.putInt(0); // Reachable time, unspecified packet.putInt(100); // Retrans time 100ms. Loading @@ -1367,6 +1377,10 @@ public class IpClientIntegrationTest { return packet; } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { return buildRaPacket((short) 1800, options); } private void disableIpv6ProvisioningDelays() throws Exception { // Speed up the test by disabling DAD and removing router_solicitation_delay. // We don't need to restore the default value because the interface is removed in tearDown. Loading Loading @@ -2153,4 +2167,73 @@ public class IpClientIntegrationTest { doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); } private void doDualStackProvisioning() throws Exception { when(mCm.shouldAvoidBadWifi()).thenReturn(true); final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .build(); // Accelerate DHCP handshake to shorten test duration, not strictly necessary. mDependencies.setDhcpRapidCommitEnabled(true); mIpc.startProvisioning(config); final InOrder inOrder = inOrder(mCb); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); final String dnsServer = "2001:4860:4860::64"; final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); final ByteBuffer ra = buildRaPacket(pio, rdnss); doIpv6OnlyProvisioning(inOrder, ra); // Start IPv4 provisioning and wait until entire provisioning completes. handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; lpFuture.complete(x); return true; })); final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(lp); assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); assertTrue(lp.getDnsServers().contains(SERVER_ADDR)); reset(mCb); } @Test public void testIgnoreIpv6ProvisioningLoss() throws Exception { doDualStackProvisioning(); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers // have been removed, then verify if there is IPv4-only info left in the LinkProperties. sendRouterAdvertisementWithZeroLifetime(); verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( argThat(x -> { final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1 && x.getDnsServers().size() == 1 && x.getAddresses().get(0) instanceof Inet4Address && x.getDnsServers().get(0) instanceof Inet4Address); if (!isOnlyIPv4Provisioned) return false; lpFuture.complete(x); return true; })); final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(lp); assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); } @Test public void testDualStackProvisioning() throws Exception { doDualStackProvisioning(); verify(mCb, never()).onProvisioningFailure(any()); } }
tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; Loading Loading @@ -1229,6 +1230,32 @@ public class NetworkMonitorTest { verify(mCapportApiConnection, times(1)).getResponseCode(); } @Test public void testIsCaptivePortal_NoRevalidationBeforeNetworkConnected() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); final NetworkMonitor nm = makeCellMeteredNetworkMonitor(); final LinkProperties lp = makeCapportLPs(); // LinkProperties changed, but NM should not revalidate before notifyNetworkConnected nm.notifyLinkPropertiesChanged(lp); verify(mHttpConnection, after(100).never()).getResponseCode(); verify(mHttpsConnection, never()).getResponseCode(); verify(mCapportApiConnection, never()).getResponseCode(); setValidProbes(); setApiContent(mCapportApiConnection, "{'captive': true, " + "'user-portal-url': '" + TEST_LOGIN_URL + "'}"); // After notifyNetworkConnected, validation uses the capport API contents nm.notifyNetworkConnected(lp, CELL_METERED_CAPABILITIES); verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); verify(mHttpConnection, never()).getResponseCode(); verify(mCapportApiConnection).getResponseCode(); } @Test public void testIsCaptivePortal_CapportApiNotPortalNotValidated() throws Exception { assumeTrue(CaptivePortalDataShimImpl.isSupported()); Loading