Merge "Fix missing onLost NetworkCallbacks when network loses capability" into mnc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dc8ff8f..ba63969 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -892,7 +892,6 @@
      *
      * @deprecated Deprecated in favor of the cleaner
      *             {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
-     * @removed
      */
     public int startUsingNetworkFeature(int networkType, String feature) {
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -939,8 +938,7 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      *
-     * @deprecated Deprecated in favor of the cleaner {@link unregisterNetworkCallback} API.
-     * @removed
+     * @deprecated Deprecated in favor of the cleaner {@link #unregisterNetworkCallback} API.
      */
     public int stopUsingNetworkFeature(int networkType, String feature) {
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1220,7 +1218,6 @@
      * @deprecated Deprecated in favor of the
      *             {@link #requestNetwork(NetworkRequest, NetworkCallback)},
      *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
-     * @removed
      */
     public boolean requestRouteToHost(int networkType, int hostAddress) {
         return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1239,7 +1236,6 @@
      * @hide
      * @deprecated Deprecated in favor of the {@link #requestNetwork} and
      *             {@link #bindProcessToNetwork} API.
-     * @removed
      */
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         try {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index cfd5bf1..c4de4a2 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -662,6 +662,17 @@
     }
 
     /**
+     * Returns true if this link or any of its stacked interfaces has an IPv4 address.
+     *
+     * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+     */
+    private boolean hasIPv4AddressOnInterface(String iface) {
+        return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+                (iface != null && mStackedLinks.containsKey(iface) &&
+                        mStackedLinks.get(iface).hasIPv4Address());
+    }
+
+    /**
      * Returns true if this link has a global preferred IPv6 address.
      *
      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
@@ -792,7 +803,7 @@
 
         if (ip instanceof Inet4Address) {
             // For IPv4, it suffices for now to simply have any address.
-            return hasIPv4Address();
+            return hasIPv4AddressOnInterface(bestRoute.getInterface());
         } else if (ip instanceof Inet6Address) {
             if (ip.isLinkLocalAddress()) {
                 // For now, just make sure link-local destinations have
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index e6fc1ea..808a882 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -148,6 +148,13 @@
      */
     public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
 
+    /**
+     * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
+     * automatically reconnecting to this network (e.g. via autojoin).  Happens
+     * when user selects "No" option on the "Stay connected?" dialog box.
+     */
+    public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 11;
+
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null);
@@ -240,6 +247,11 @@
             }
             case CMD_SAVE_ACCEPT_UNVALIDATED: {
                 saveAcceptUnvalidated(msg.arg1 != 0);
+                break;
+            }
+            case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+                preventAutomaticReconnect();
+                break;
             }
         }
     }
@@ -365,6 +377,15 @@
     protected void saveAcceptUnvalidated(boolean accept) {
     }
 
+    /**
+     * Called when the user asks to not stay connected to this network because it was found to not
+     * provide Internet access.  Usually followed by call to {@code unwanted}.  The transport is
+     * responsible for making sure the device does not automatically reconnect to the same network
+     * after the {@code unwanted} call.
+     */
+    protected void preventAutomaticReconnect() {
+    }
+
     protected void log(String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6bc1a59..da94532 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1772,7 +1772,7 @@
                 // Start gathering diagnostic information.
                 netDiags.add(new NetworkDiagnostics(
                         nai.network,
-                        new LinkProperties(nai.linkProperties),
+                        new LinkProperties(nai.linkProperties),  // Must be a copy.
                         DIAG_TIME_MS));
             }
 
@@ -2382,14 +2382,10 @@
         }
 
         if (!accept) {
-            // Tell the NetworkAgent that the network does not have Internet access (because that's
-            // what we just told the user). This will hint to Wi-Fi not to autojoin this network in
-            // the future. We do this now because NetworkMonitor might not yet have finished
-            // validating and thus we might not yet have received an EVENT_NETWORK_TESTED.
-            nai.asyncChannel.sendMessage(NetworkAgent.CMD_REPORT_NETWORK_STATUS,
-                    NetworkAgent.INVALID_NETWORK, 0, null);
-            // TODO: Tear the network down once we have determined how to tell WifiStateMachine not
-            // to reconnect to it immediately. http://b/20739299
+            // Tell the NetworkAgent to not automatically reconnect to the network.
+            nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
+            // Teardown the nework.
+            teardownUnneededNetwork(nai);
         }
 
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 74ba404..aca6991 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -20,6 +20,7 @@
 
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.os.SystemClock;
 import android.system.ErrnoException;
@@ -79,6 +80,10 @@
 public class NetworkDiagnostics {
     private static final String TAG = "NetworkDiagnostics";
 
+    private static final InetAddress TEST_DNS4 = NetworkUtils.numericToInetAddress("8.8.8.8");
+    private static final InetAddress TEST_DNS6 = NetworkUtils.numericToInetAddress(
+            "2001:4860:4860::8888");
+
     // For brevity elsewhere.
     private static final long now() {
         return SystemClock.elapsedRealtime();
@@ -156,6 +161,21 @@
         mStartTime = now();
         mDeadlineTime = mStartTime + mTimeoutMs;
 
+        // Hardcode measurements to TEST_DNS4 and TEST_DNS6 in order to test off-link connectivity.
+        // We are free to modify mLinkProperties with impunity because ConnectivityService passes us
+        // a copy and not the original object. It's easier to do it this way because we don't need
+        // to check whether the LinkProperties already contains these DNS servers because
+        // LinkProperties#addDnsServer checks for duplicates.
+        if (mLinkProperties.isReachable(TEST_DNS4)) {
+            mLinkProperties.addDnsServer(TEST_DNS4);
+        }
+        // TODO: we could use mLinkProperties.isReachable(TEST_DNS6) here, because we won't set any
+        // DNS servers for which isReachable() is false, but since this is diagnostic code, be extra
+        // careful.
+        if (mLinkProperties.hasGlobalIPv6Address() || mLinkProperties.hasIPv6DefaultRoute()) {
+            mLinkProperties.addDnsServer(TEST_DNS6);
+        }
+
         for (RouteInfo route : mLinkProperties.getRoutes()) {
             if (route.hasGateway()) {
                 prepareIcmpMeasurement(route.getGateway());