Merge changes I3ebf0cec,I8635472c into lmp-dev

* changes:
  Add a throw route to the VPN endpoint.
  Support non-unicast route types: unreachable and throw.
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index f1fa3eb..b268986 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -172,7 +172,7 @@
     /**
      * Returns a string representation of this {@code IpPrefix}.
      *
-     * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}.
+     * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}.
      */
     public String toString() {
         try {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3d6a132..662c576 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -381,7 +381,8 @@
         return new RouteInfo(
             route.getDestination(),
             route.getGateway(),
-            mIfaceName);
+            mIfaceName,
+            route.getType());
     }
 
     /**
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index a4ec80c..cfd20a0 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -62,6 +62,23 @@
      */
     private final String mInterface;
 
+
+    /** Unicast route. @hide */
+    public static final int RTN_UNICAST = 1;
+
+    /** Unreachable route. @hide */
+    public static final int RTN_UNREACHABLE = 7;
+
+    /** Throw route. @hide */
+    public static final int RTN_THROW = 9;
+
+    /**
+     * The type of this route; one of the RTN_xxx constants above.
+     */
+    private final int mType;
+
+    // Derived data members.
+    // TODO: remove these.
     private final boolean mIsHost;
     private final boolean mHasGateway;
 
@@ -82,7 +99,26 @@
      *
      * @hide
      */
-    public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+    public RouteInfo(IpPrefix destination, InetAddress gateway, String iface, int type) {
+        switch (type) {
+            case RTN_UNICAST:
+            case RTN_UNREACHABLE:
+            case RTN_THROW:
+                // TODO: It would be nice to ensure that route types that don't have nexthops or
+                // interfaces, such as unreachable or throw, can't be created if an interface or
+                // a gateway is specified. This is a bit too complicated to do at the moment
+                // because:
+                //
+                // - LinkProperties sets the interface on routes added to it, and modifies the
+                //   interfaces of all the routes when its interface name changes.
+                // - Even when the gateway is null, we store a non-null gateway here.
+                //
+                // For now, we just rely on the code that sets routes to do things properly.
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown route type " + type);
+        }
+
         if (destination == null) {
             if (gateway != null) {
                 if (gateway instanceof Inet4Address) {
@@ -117,10 +153,18 @@
         mDestination = destination;  // IpPrefix objects are immutable.
         mGateway = gateway;          // InetAddress objects are immutable.
         mInterface = iface;          // Strings are immutable.
+        mType = type;
         mIsHost = isHost();
     }
 
     /**
+     *  @hide
+     */
+    public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+        this(destination, gateway, iface, RTN_UNICAST);
+    }
+
+    /**
      * @hide
      */
     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
@@ -150,6 +194,8 @@
 
     /**
      * @hide
+     *
+     * TODO: Remove this.
      */
     public RouteInfo(LinkAddress destination, InetAddress gateway) {
         this(destination, gateway, null);
@@ -188,6 +234,13 @@
     /**
      * @hide
      */
+    public RouteInfo(IpPrefix destination, int type) {
+        this(destination, null, null, type);
+    }
+
+    /**
+     * @hide
+     */
     public static RouteInfo makeHostRoute(InetAddress host, String iface) {
         return makeHostRoute(host, null, iface);
     }
@@ -249,12 +302,23 @@
     }
 
     /**
+     * Retrieves the type of this route.
+     *
+     * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
+     *
+     * @hide
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
      * Indicates if this route is a default route (ie, has no destination specified).
      *
      * @return {@code true} if the destination has a prefix length of 0.
      */
     public boolean isDefaultRoute() {
-        return mDestination.getPrefixLength() == 0;
+        return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0;
     }
 
     /**
@@ -345,9 +409,18 @@
     public String toString() {
         String val = "";
         if (mDestination != null) val = mDestination.toString();
-        val += " ->";
-        if (mGateway != null) val += " " + mGateway.getHostAddress();
-        if (mInterface != null) val += " " + mInterface;
+        if (mType == RTN_UNREACHABLE) {
+            val += " unreachable";
+        } else if (mType == RTN_THROW) {
+            val += " throw";
+        } else {
+            val += " ->";
+            if (mGateway != null) val += " " + mGateway.getHostAddress();
+            if (mInterface != null) val += " " + mInterface;
+            if (mType != RTN_UNICAST) {
+                val += " unknown type " + mType;
+            }
+        }
         return val;
     }
 
@@ -364,7 +437,8 @@
 
         return Objects.equals(mDestination, target.getDestination()) &&
                 Objects.equals(mGateway, target.getGateway()) &&
-                Objects.equals(mInterface, target.getInterface());
+                Objects.equals(mInterface, target.getInterface()) &&
+                mType == target.getType();
     }
 
     /**
@@ -373,7 +447,8 @@
     public int hashCode() {
         return (mDestination.hashCode() * 41)
                 + (mGateway == null ? 0 :mGateway.hashCode() * 47)
-                + (mInterface == null ? 0 :mInterface.hashCode() * 67);
+                + (mInterface == null ? 0 :mInterface.hashCode() * 67)
+                + (mType * 71);
     }
 
     /**
@@ -391,6 +466,7 @@
         byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
         dest.writeByteArray(gatewayBytes);
         dest.writeString(mInterface);
+        dest.writeInt(mType);
     }
 
     /**
@@ -408,8 +484,9 @@
             } catch (UnknownHostException e) {}
 
             String iface = in.readString();
+            int type = in.readInt();
 
-            return new RouteInfo(dest, gateway, iface);
+            return new RouteInfo(dest, gateway, iface, type);
         }
 
         public RouteInfo[] newArray(int size) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1318f66..6144078 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -25,6 +25,9 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_TETHERING;
+import static android.net.RouteInfo.RTN_THROW;
+import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
@@ -955,11 +958,21 @@
         final Command cmd = new Command("network", "route", action, netId);
 
         // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
-        final LinkAddress la = route.getDestinationLinkAddress();
         cmd.appendArg(route.getInterface());
-        cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
-        if (route.hasGateway()) {
-            cmd.appendArg(route.getGateway().getHostAddress());
+        cmd.appendArg(route.getDestination().toString());
+
+        switch (route.getType()) {
+            case RouteInfo.RTN_UNICAST:
+                if (route.hasGateway()) {
+                    cmd.appendArg(route.getGateway().getHostAddress());
+                }
+                break;
+            case RouteInfo.RTN_UNREACHABLE:
+                cmd.appendArg("unreachable");
+                break;
+            case RouteInfo.RTN_THROW:
+                cmd.appendArg("throw");
+                break;
         }
 
         try {
@@ -2129,6 +2142,7 @@
         modifyAddressFamily("remove", family, netId, iface);
     }
 
+    // TODO: get rid of this and add RTN_UNREACHABLE routes instead.
     private void modifyAddressFamily(String action, int family, int netId, String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index afc2a39..4100ae9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
 import static android.os.UserHandle.PER_USER_RANGE;
+import static android.net.RouteInfo.RTN_THROW;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 
@@ -38,6 +39,7 @@
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LocalSocket;
@@ -1220,7 +1222,7 @@
 
                 // Now we are connected. Read and parse the new state.
                 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
-                if (parameters.length != 6) {
+                if (parameters.length != 7) {
                     throw new IllegalStateException("Cannot parse the state");
                 }
 
@@ -1249,6 +1251,23 @@
                     }
                 }
 
+                // Add a throw route for the VPN server endpoint, if one was specified.
+                String endpoint = parameters[5];
+                if (!endpoint.isEmpty()) {
+                    try {
+                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
+                        if (addr instanceof Inet4Address) {
+                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
+                        } else if (addr instanceof Inet6Address) {
+                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
+                        } else {
+                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
+                    }
+                }
+
                 // Here is the last step and it must be done synchronously.
                 synchronized (Vpn.this) {
                     // Set the start time