Add RouteInfo objects for tracking routes.

Used to have list of gateways for default routes, but general static routes
should be supported.

Change-Id: I01730142c6139f2b833b9d48f5381d2d320b69f6
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index e88292f..61acf2b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -54,7 +54,7 @@
     String mIfaceName;
     private Collection<LinkAddress> mLinkAddresses;
     private Collection<InetAddress> mDnses;
-    private Collection<InetAddress> mGateways;
+    private Collection<RouteInfo> mRoutes;
     private ProxyProperties mHttpProxy;
 
     public LinkProperties() {
@@ -67,7 +67,7 @@
             mIfaceName = source.getInterfaceName();
             mLinkAddresses = source.getLinkAddresses();
             mDnses = source.getDnses();
-            mGateways = source.getGateways();
+            mRoutes = source.getRoutes();
             mHttpProxy = new ProxyProperties(source.getHttpProxy());
         }
     }
@@ -104,11 +104,11 @@
         return Collections.unmodifiableCollection(mDnses);
     }
 
-    public void addGateway(InetAddress gateway) {
-        if (gateway != null) mGateways.add(gateway);
+    public void addRoute(RouteInfo route) {
+        if (route != null) mRoutes.add(route);
     }
-    public Collection<InetAddress> getGateways() {
-        return Collections.unmodifiableCollection(mGateways);
+    public Collection<RouteInfo> getRoutes() {
+        return Collections.unmodifiableCollection(mRoutes);
     }
 
     public void setHttpProxy(ProxyProperties proxy) {
@@ -122,7 +122,7 @@
         mIfaceName = null;
         mLinkAddresses = new ArrayList<LinkAddress>();
         mDnses = new ArrayList<InetAddress>();
-        mGateways = new ArrayList<InetAddress>();
+        mRoutes = new ArrayList<RouteInfo>();
         mHttpProxy = null;
     }
 
@@ -146,12 +146,12 @@
         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
         dns += "] ";
 
-        String gateways = "Gateways: [";
-        for (InetAddress gw : mGateways) gateways += gw.getHostAddress() + ",";
-        gateways += "] ";
+        String routes = "Routes: [";
+        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
+        routes += "] ";
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
 
-        return ifaceName + linkAddresses + gateways + dns + proxy;
+        return ifaceName + linkAddresses + routes + dns + proxy;
     }
 
 
@@ -177,7 +177,7 @@
 
         boolean sameAddresses;
         boolean sameDnses;
-        boolean sameGateways;
+        boolean sameRoutes;
 
         LinkProperties target = (LinkProperties) obj;
 
@@ -190,12 +190,12 @@
         sameDnses = (mDnses.size() == targetDnses.size()) ?
                 mDnses.containsAll(targetDnses) : false;
 
-        Collection<InetAddress> targetGateways = target.getGateways();
-        sameGateways = (mGateways.size() == targetGateways.size()) ?
-                mGateways.containsAll(targetGateways) : false;
+        Collection<RouteInfo> targetRoutes = target.getRoutes();
+        sameRoutes = (mRoutes.size() == targetRoutes.size()) ?
+                mRoutes.containsAll(targetRoutes) : false;
 
         return
-            sameAddresses && sameDnses && sameGateways
+            sameAddresses && sameDnses && sameRoutes
             && TextUtils.equals(getInterfaceName(), target.getInterfaceName())
             && (getHttpProxy() == null ? target.getHttpProxy() == null :
                 getHttpProxy().equals(target.getHttpProxy()));
@@ -211,7 +211,7 @@
         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
                 + mLinkAddresses.size() * 31
                 + mDnses.size() * 37
-                + mGateways.size() * 41
+                + mRoutes.size() * 41
                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
     }
 
@@ -231,9 +231,9 @@
             dest.writeByteArray(d.getAddress());
         }
 
-        dest.writeInt(mGateways.size());
-        for(InetAddress gw : mGateways) {
-            dest.writeByteArray(gw.getAddress());
+        dest.writeInt(mRoutes.size());
+        for(RouteInfo route : mRoutes) {
+            dest.writeParcelable(route, flags);
         }
 
         if (mHttpProxy != null) {
@@ -272,9 +272,7 @@
                 }
                 addressCount = in.readInt();
                 for (int i=0; i<addressCount; i++) {
-                    try {
-                        netProp.addGateway(InetAddress.getByAddress(in.createByteArray()));
-                    } catch (UnknownHostException e) { }
+                    netProp.addRoute((RouteInfo)in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
new file mode 100644
index 0000000..5b10531
--- /dev/null
+++ b/core/java/android/net/RouteInfo.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+/**
+ * A simple container for route information.
+ *
+ * @hide
+ */
+public class RouteInfo implements Parcelable {
+    /**
+     * The IP destination address for this route.
+     */
+    private final LinkAddress mDestination;
+
+    /**
+     * The gateway address for this route.
+     */
+    private final InetAddress mGateway;
+
+    private final boolean mIsDefault;
+
+    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+        if (destination == null) {
+            try {
+                if ((gateway != null) && (gateway instanceof Inet4Address)) {
+                    destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
+                } else {
+                    destination = new LinkAddress(InetAddress.getByName("::0"), 128);
+                }
+            } catch (Exception e) {}
+        }
+        mDestination = destination;
+        mGateway = gateway;
+        mIsDefault = isDefault();
+    }
+
+    public RouteInfo(InetAddress gateway) {
+        LinkAddress destination = null;
+        try {
+            if ((gateway != null) && (gateway instanceof Inet4Address)) {
+                destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
+            } else {
+                destination = new LinkAddress(InetAddress.getByName("::0"), 128);
+            }
+        } catch (Exception e) {}
+        mDestination = destination;
+        mGateway = gateway;
+        mIsDefault = isDefault();
+    }
+
+    private boolean isDefault() {
+        boolean val = false;
+        if (mGateway != null) {
+            if (mGateway instanceof Inet4Address) {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 32);
+            } else {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 128);
+            }
+        }
+        return val;
+    }
+
+    public LinkAddress getDestination() {
+        return mDestination;
+    }
+
+    public InetAddress getGateway() {
+        return mGateway;
+    }
+
+    public boolean isDefaultRoute() {
+        return mIsDefault;
+    }
+
+    public String toString() {
+        String val = "";
+        if (mDestination != null) val = mDestination.toString();
+        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+        return val;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDestination == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mDestination.getAddress().getAddress());
+            dest.writeInt(mDestination.getNetworkPrefixLength());
+        }
+
+        if (mGateway == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mGateway.getAddress());
+        }
+    }
+
+    public static final Creator<RouteInfo> CREATOR =
+        new Creator<RouteInfo>() {
+        public RouteInfo createFromParcel(Parcel in) {
+            InetAddress destAddr = null;
+            int prefix = 0;
+            InetAddress gateway = null;
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+                prefix = in.readInt();
+
+                try {
+                    destAddr = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+
+                try {
+                    gateway = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            LinkAddress dest = null;
+
+            if (destAddr != null) {
+                dest = new LinkAddress(destAddr, prefix);
+            }
+
+            return new RouteInfo(dest, gateway);
+        }
+
+        public RouteInfo[] newArray(int size) {
+            return new RouteInfo[size];
+        }
+    };
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 3adf770..33ba26a 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -58,7 +58,6 @@
     jclass dhcpInfoInternalClass;
     jmethodID constructorId;
     jfieldID ipaddress;
-    jfieldID gateway;
     jfieldID prefixLength;
     jfieldID dns1;
     jfieldID dns2;
@@ -165,7 +164,30 @@
     env->ReleaseStringUTFChars(ifname, nameStr);
     if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
         env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
-        env->SetObjectField(info, dhcpInfoInternalFieldIds.gateway, env->NewStringUTF(gateway));
+
+        // set the gateway
+        jclass cls = env->FindClass("java/net/InetAddress");
+        jmethodID method = env->GetStaticMethodID(cls, "getByName",
+                "(Ljava/lang/String;)Ljava/net/InetAddress;");
+        jvalue args[1];
+        args[0].l = env->NewStringUTF(gateway);
+        jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args);
+
+        if (!env->ExceptionOccurred()) {
+            cls = env->FindClass("android/net/RouteInfo");
+            method = env->GetMethodID(cls, "<init>", "(Ljava/net/InetAddress;)V");
+            args[0].l = inetAddressObject;
+            jobject routeInfoObject = env->NewObjectA(cls, method, args);
+
+            cls = env->FindClass("android/net/DhcpInfoInternal");
+            method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V");
+            args[0].l = routeInfoObject;
+            env->CallVoidMethodA(info, method, args);
+        } else {
+            // if we have an exception (host not found perhaps), just don't add the route
+            env->ExceptionClear();
+        }
+
         env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength);
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1));
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2));
@@ -233,7 +255,6 @@
     if (dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
         dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "<init>", "()V");
         dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
-        dhcpInfoInternalFieldIds.gateway = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "gateway", "Ljava/lang/String;");
         dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "prefixLength", "I");
         dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
         dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 50666b4..e3b6b5f 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.test.suitebuilder.annotation.SmallTest;
 import junit.framework.TestCase;
 
@@ -55,8 +56,8 @@
             source.addDns(NetworkUtils.numericToInetAddress(DNS1));
             source.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // set 2 gateways
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             LinkProperties target = new LinkProperties();
 
@@ -68,8 +69,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             assertTrue(source.equals(target));
             assertTrue(source.hashCode() == target.hashCode());
@@ -83,8 +84,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -96,8 +97,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -109,8 +110,8 @@
             // change dnses
             target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -122,8 +123,8 @@
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // change gateway
-            target.addGateway(NetworkUtils.numericToInetAddress("75.208.8.2"));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
         } catch (Exception e) {
@@ -146,8 +147,8 @@
             source.addDns(NetworkUtils.numericToInetAddress(DNS1));
             source.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // set 2 gateways
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             LinkProperties target = new LinkProperties();
             // Exchange order
@@ -158,8 +159,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV4), 32));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
 
             assertTrue(source.equals(target));
             assertTrue(source.hashCode() == target.hashCode());
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 998382c..d77ab60 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyProperties;
+import android.net.RouteInfo;
 import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
@@ -1413,14 +1414,19 @@
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
         if (TextUtils.isEmpty(interfaceName)) return;
-        for (InetAddress gateway : p.getGateways()) {
+        for (RouteInfo route : p.getRoutes()) {
 
-            if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
-                    NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
-                if (DBG) {
-                    NetworkInfo networkInfo = nt.getNetworkInfo();
-                    log("addDefaultRoute for " + networkInfo.getTypeName() +
-                            " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress());
+            //TODO - handle non-default routes
+            if (route.isDefaultRoute()) {
+                InetAddress gateway = route.getGateway();
+                if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
+                        NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
+                    if (DBG) {
+                        NetworkInfo networkInfo = nt.getNetworkInfo();
+                        log("addDefaultRoute for " + networkInfo.getTypeName() +
+                                " (" + interfaceName + "), GatewayAddr=" +
+                                gateway.getHostAddress());
+                    }
                 }
             }
         }