Add better routes for secondary DNS

We'd been doing no-gateway hostroutes for dns servers on secondary nets, but on
some devices (multi-homed stingray) this is a problem.  Add gateway-ed hostroutes
instead so the BP can do it's nonstandard "magical" demultiplexing.

bug:5011392
Change-Id: Ia48f69c8ddf2a37cfb8f014f078f96bf601d2ddb
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 9826bec..132f3ba 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -58,8 +58,8 @@
     private ProxyProperties mHttpProxy;
 
     public static class CompareResult<T> {
-        public ArrayList<T> removed = new ArrayList<T>();
-        public ArrayList<T> added = new ArrayList<T>();
+        public Collection<T> removed = new ArrayList<T>();
+        public Collection<T> added = new ArrayList<T>();
 
         @Override
         public String toString() {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 2bbf92e..06be27b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -963,7 +963,7 @@
         try {
             InetAddress addr = InetAddress.getByAddress(hostAddress);
             LinkProperties lp = tracker.getLinkProperties();
-            return addRoute(lp, RouteInfo.makeHostRoute(addr));
+            return addRouteToAddress(lp, addr);
         } catch (UnknownHostException e) {}
         return false;
     }
@@ -976,6 +976,31 @@
         return modifyRoute(p.getInterfaceName(), p, r, 0, false);
     }
 
+    private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
+        return modifyRouteToAddress(lp, addr, true);
+    }
+
+    private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
+        return modifyRouteToAddress(lp, addr, false);
+    }
+
+    private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd) {
+        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
+        if (bestRoute == null) {
+            bestRoute = RouteInfo.makeHostRoute(addr);
+        } else {
+            if (bestRoute.getGateway().equals(addr)) {
+                // if there is no better route, add the implied hostroute for our gateway
+                bestRoute = RouteInfo.makeHostRoute(addr);
+            } else {
+                // if we will connect to this through another route, add a direct route
+                // to it's gateway
+                bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway());
+            }
+        }
+        return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd);
+    }
+
     private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
             boolean doAdd) {
         if ((ifaceName == null) || (lp == null) || (r == null)) return false;
@@ -1547,49 +1572,50 @@
      */
     private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) {
         Collection<RouteInfo> routesToAdd = null;
-        CompareResult<InetAddress> dnsDiff = null;
-
+        CompareResult<InetAddress> dnsDiff = new CompareResult<InetAddress>();
+        CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
         if (curLp != null) {
             // check for the delta between the current set and the new
-            CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp);
+            routeDiff = curLp.compareRoutes(newLp);
             dnsDiff = curLp.compareDnses(newLp);
-
-            for (RouteInfo r : routeDiff.removed) {
-                if (isLinkDefault || ! r.isDefaultRoute()) {
-                    removeRoute(curLp, r);
-                }
-            }
-            routesToAdd = routeDiff.added;
+        } else if (newLp != null) {
+            routeDiff.added = newLp.getRoutes();
+            dnsDiff.added = newLp.getDnses();
         }
 
-        if (newLp != null) {
-            // if we didn't get a diff from cur -> new, then just use the new
-            if (routesToAdd == null) {
-                routesToAdd = newLp.getRoutes();
+        for (RouteInfo r : routeDiff.removed) {
+            if (isLinkDefault || ! r.isDefaultRoute()) {
+                removeRoute(curLp, r);
             }
+        }
 
-            for (RouteInfo r :  routesToAdd) {
-                if (isLinkDefault || ! r.isDefaultRoute()) {
-                    addRoute(newLp, r);
-                }
+        for (RouteInfo r :  routeDiff.added) {
+            if (isLinkDefault || ! r.isDefaultRoute()) {
+                addRoute(newLp, r);
             }
         }
 
         if (!isLinkDefault) {
             // handle DNS routes
-            Collection<InetAddress> dnsToAdd = null;
-            if (dnsDiff != null) {
-                dnsToAdd = dnsDiff.added;
-                for (InetAddress dnsAddress : dnsDiff.removed) {
-                    removeRoute(curLp, RouteInfo.makeHostRoute(dnsAddress));
+            if (routeDiff.removed.size() == 0 && routeDiff.added.size() == 0) {
+                // no change in routes, check for change in dns themselves
+                for (InetAddress oldDns : dnsDiff.removed) {
+                    removeRouteToAddress(curLp, oldDns);
                 }
-            }
-            if (newLp != null) {
-                if (dnsToAdd == null) {
-                    dnsToAdd = newLp.getDnses();
+                for (InetAddress newDns : dnsDiff.added) {
+                    addRouteToAddress(newLp, newDns);
                 }
-                for(InetAddress dnsAddress : dnsToAdd) {
-                    addRoute(newLp, RouteInfo.makeHostRoute(dnsAddress));
+            } else {
+                // routes changed - remove all old dns entries and add new
+                if (curLp != null) {
+                    for (InetAddress oldDns : curLp.getDnses()) {
+                        removeRouteToAddress(curLp, oldDns);
+                    }
+                }
+                if (newLp != null) {
+                    for (InetAddress newDns : newLp.getDnses()) {
+                        addRouteToAddress(newLp, newDns);
+                    }
                 }
             }
         }