Merge "Push firewall rules up to ConnectivityService." into mnc-dr-dev
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f7f8585..77837b7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2270,8 +2270,9 @@
         mNetworkRequestInfoLogs.log("REGISTER " + nri);
         if (!nri.isRequest) {
             for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
-                if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
-                    updateSignalStrengthThresholds(network);
+                if (nri.request.networkCapabilities.hasSignalStrength() &&
+                        network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                    updateSignalStrengthThresholds(network, "REGISTER", nri.request);
                 }
             }
         }
@@ -2388,8 +2389,9 @@
                 // if this listen request applies and remove it.
                 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                     nai.networkRequests.remove(nri.request.requestId);
-                    if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
-                        updateSignalStrengthThresholds(nai);
+                    if (nri.request.networkCapabilities.hasSignalStrength() &&
+                            nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                        updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
                     }
                 }
             }
@@ -3639,9 +3641,24 @@
         return new ArrayList<Integer>(thresholds);
     }
 
-    private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+    private void updateSignalStrengthThresholds(
+            NetworkAgentInfo nai, String reason, NetworkRequest request) {
+        ArrayList<Integer> thresholdsArray = getSignalStrengthThresholds(nai);
         Bundle thresholds = new Bundle();
-        thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai));
+        thresholds.putIntegerArrayList("thresholds", thresholdsArray);
+
+        // TODO: Switch to VDBG.
+        if (DBG) {
+            String detail;
+            if (request != null && request.networkCapabilities.hasSignalStrength()) {
+                detail = reason + " " + request.networkCapabilities.getSignalStrength();
+            } else {
+                detail = reason;
+            }
+            log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
+                    detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+        }
+
         nai.asyncChannel.sendMessage(
                 android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
                 0, 0, thresholds);
@@ -4624,7 +4641,7 @@
             // so we could decide to tear it down immediately afterwards. That's fine though - on
             // disconnection NetworkAgents should stop any signal strength monitoring they have been
             // doing.
-            updateSignalStrengthThresholds(networkAgent);
+            updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
             // Consider network even though it is not yet validated.
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index aca6991..5fd39c0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,7 @@
 
 import static android.system.OsConstants.*;
 
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkUtils;
@@ -27,6 +28,7 @@
 import android.system.Os;
 import android.system.StructTimeval;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -149,6 +151,8 @@
     }
 
     private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
+    private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
+            new HashMap<>();
     private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
     private final String mDescription;
 
@@ -178,7 +182,11 @@
 
         for (RouteInfo route : mLinkProperties.getRoutes()) {
             if (route.hasGateway()) {
-                prepareIcmpMeasurement(route.getGateway());
+                InetAddress gateway = route.getGateway();
+                prepareIcmpMeasurement(gateway);
+                if (route.isIPv6Default()) {
+                    prepareExplicitSourceIcmpMeasurements(gateway);
+                }
             }
         }
         for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
@@ -213,6 +221,20 @@
         }
     }
 
+    private void prepareExplicitSourceIcmpMeasurements(InetAddress target) {
+        for (LinkAddress l : mLinkProperties.getLinkAddresses()) {
+            InetAddress source = l.getAddress();
+            if (source instanceof Inet6Address && l.isGlobalPreferred()) {
+                Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target);
+                if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) {
+                    Measurement measurement = new Measurement();
+                    measurement.thread = new Thread(new IcmpCheck(source, target, measurement));
+                    mExplicitSourceIcmpChecks.put(srcTarget, measurement);
+                }
+            }
+        }
+    }
+
     private void prepareDnsMeasurement(InetAddress target) {
         if (!mDnsUdpChecks.containsKey(target)) {
             Measurement measurement = new Measurement();
@@ -222,13 +244,16 @@
     }
 
     private int totalMeasurementCount() {
-        return mIcmpChecks.size() + mDnsUdpChecks.size();
+        return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size();
     }
 
     private void startMeasurements() {
         for (Measurement measurement : mIcmpChecks.values()) {
             measurement.thread.start();
         }
+        for (Measurement measurement : mExplicitSourceIcmpChecks.values()) {
+            measurement.thread.start();
+        }
         for (Measurement measurement : mDnsUdpChecks.values()) {
             measurement.thread.start();
         }
@@ -261,6 +286,10 @@
                 pw.println(entry.getValue().toString());
             }
         }
+        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
+                mExplicitSourceIcmpChecks.entrySet()) {
+            pw.println(entry.getValue().toString());
+        }
         for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
             if (entry.getKey() instanceof Inet4Address) {
                 pw.println(entry.getValue().toString());
@@ -276,13 +305,15 @@
 
 
     private class SimpleSocketCheck implements Closeable {
+        protected final InetAddress mSource;  // Usually null.
         protected final InetAddress mTarget;
         protected final int mAddressFamily;
         protected final Measurement mMeasurement;
         protected FileDescriptor mFileDescriptor;
         protected SocketAddress mSocketAddress;
 
-        protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+        protected SimpleSocketCheck(
+                InetAddress source, InetAddress target, Measurement measurement) {
             mMeasurement = measurement;
 
             if (target instanceof Inet6Address) {
@@ -301,6 +332,14 @@
                 mTarget = target;
                 mAddressFamily = AF_INET;
             }
+
+            // We don't need to check the scope ID here because we currently only do explicit-source
+            // measurements from global IPv6 addresses.
+            mSource = source;
+        }
+
+        protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+            this(null, target, measurement);
         }
 
         protected void setupSocket(
@@ -314,6 +353,9 @@
                     SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
             // TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
             mNetwork.bindSocket(mFileDescriptor);
+            if (mSource != null) {
+                Os.bind(mFileDescriptor, mSource, 0);
+            }
             Os.connect(mFileDescriptor, mTarget, dstPort);
             mSocketAddress = Os.getsockname(mFileDescriptor);
         }
@@ -343,8 +385,8 @@
         private final int mProtocol;
         private final int mIcmpType;
 
-        public IcmpCheck(InetAddress target, Measurement measurement) {
-            super(target, measurement);
+        public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) {
+            super(source, target, measurement);
 
             if (mAddressFamily == AF_INET6) {
                 mProtocol = IPPROTO_ICMPV6;
@@ -359,6 +401,10 @@
             mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
         }
 
+        public IcmpCheck(InetAddress target, Measurement measurement) {
+            this(null, target, measurement);
+        }
+
         @Override
         public void run() {
             // Check if this measurement has already failed during setup.