Merge "Don't accept RAs in link-local only mode."
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 12ab3fd..7654a05 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -1301,7 +1301,7 @@
             mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mInterfaceParams.name, "accept_ra",
                     Integer.toString(acceptRa));
         } catch (Exception e) {
-            Log.e(mTag, "Failed to set accept_ra to " + acceptRa);
+            Log.e(mTag, "Failed to set accept_ra to " + acceptRa + ": " + e);
         }
     }
 
@@ -1835,6 +1835,7 @@
     }
 
     private boolean startIPv6() {
+        setIpv6AcceptRa(mConfiguration.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL ? 0 : 2);
         return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
                 && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
                 && mInterfaceCtrl.enableIPv6();
@@ -1966,7 +1967,6 @@
         @Override
         public void enter() {
             stopAllIP();
-            setIpv6AcceptRa(2 /* accept_ra */);
             mHasDisabledIpv6OrAcceptRaOnProvLoss = false;
             mGratuitousNaTargetAddresses.clear();
 
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index 6ade54f..e9a6e9d 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -620,13 +620,7 @@
     @After
     public void tearDown() throws Exception {
         if (testSkipped()) return;
-        if (mPacketReader != null) {
-            mHandler.post(() -> mPacketReader.stop()); // Also closes the socket
-            mTapFd = null;
-        }
-        if (mPacketReaderThread != null) {
-            mPacketReaderThread.quitSafely();
-        }
+        teardownTapInterface();
         mIIpClient.shutdown();
         awaitIpClientShutdown();
     }
@@ -664,6 +658,16 @@
         mHandler.post(() -> mPacketReader.start());
     }
 
+    private void teardownTapInterface() {
+        if (mPacketReader != null) {
+            mHandler.post(() -> mPacketReader.stop());  // Also closes the socket
+            mTapFd = null;
+        }
+        if (mPacketReaderThread != null) {
+            mPacketReaderThread.quitSafely();
+        }
+    }
+
     private MacAddress getIfaceMacAddr(String ifaceName) throws IOException {
         // InterfaceParams.getByName requires CAP_NET_ADMIN: read the mac address with the shell
         final String strMacAddr = getOneLineCommandOutput(
@@ -3514,6 +3518,43 @@
         assertNotNull(route);
         assertTrue(route.getDestination().equals(new IpPrefix("fe80::/64")));
         assertTrue(route.getGateway().isAnyLocalAddress());
+
+        // Check that if an RA is received, no IP addresses, routes, or DNS servers are configured.
+        // Instead of waiting some period of time for the RA to be received and checking the
+        // LinkProperties after that, tear down the interface and wait for it to go down. Then check
+        // that no LinkProperties updates ever contained non-link-local information.
+        sendBasicRouterAdvertisement(false /* waitForRs */);
+        teardownTapInterface();
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
+        verify(mCb, never()).onLinkPropertiesChange(argThat(newLp ->
+                newLp.getDnsServers().size() != 0
+                        || newLp.getRoutes().size() > 1
+                        || newLp.hasIpv6DefaultRoute()
+                        || newLp.hasGlobalIpv6Address()
+        ));
+    }
+
+    @Test
+    public void testIPv6LinkLocalOnlyAndThenGlobal() throws Exception {
+        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+                .withoutIPv4()
+                .withIpv6LinkLocalOnly()
+                .withRandomMacAddress()
+                .build();
+        startIpClientProvisioning(config);
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(any());
+        mIIpClient.stop();
+        verifyAfterIpClientShutdown();
+        reset(mCb);
+
+        // Speed up provisioning by enabling rapid commit. TODO: why is this necessary?
+        setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */,
+                false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */);
+        config = new ProvisioningConfiguration.Builder()
+                .build();
+        startIpClientProvisioning(config);
+        performDualStackProvisioning();
+        // No exceptions? Dual-stack provisioning worked.
     }
 
     @Test