Merge "Move TrackRecordTest to libs/net"
diff --git a/staticlibs/framework/com/android/net/module/util/InetAddressUtils.java b/staticlibs/framework/com/android/net/module/util/InetAddressUtils.java
index 6300328..31d0729 100644
--- a/staticlibs/framework/com/android/net/module/util/InetAddressUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/InetAddressUtils.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
@@ -27,6 +28,8 @@
  */
 public class InetAddressUtils {
 
+    private static final int INET6_ADDR_LENGTH = 16;
+
     /**
      * Writes an InetAddress to a parcel. The address may be null. This is likely faster than
      * calling writeSerializable.
@@ -35,6 +38,13 @@
     public static void parcelInetAddress(Parcel parcel, InetAddress address, int flags) {
         byte[] addressArray = (address != null) ? address.getAddress() : null;
         parcel.writeByteArray(addressArray);
+        if (address instanceof Inet6Address) {
+            final Inet6Address v6Address = (Inet6Address) address;
+            final boolean hasScopeId = v6Address.getScopeId() != 0;
+            parcel.writeBoolean(hasScopeId);
+            if (hasScopeId) parcel.writeInt(v6Address.getScopeId());
+        }
+
     }
 
     /**
@@ -47,7 +57,14 @@
         if (addressArray == null) {
             return null;
         }
+
         try {
+            if (addressArray.length == INET6_ADDR_LENGTH) {
+                final boolean hasScopeId = in.readBoolean();
+                final int scopeId = hasScopeId ? in.readInt() : 0;
+                return Inet6Address.getByAddress(null /* host */, addressArray, scopeId);
+            }
+
             return InetAddress.getByAddress(addressArray);
         } catch (UnknownHostException e) {
             return null;
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/InetAddressUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/InetAddressUtilsTest.java
new file mode 100644
index 0000000..2736c53
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/InetAddressUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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 com.android.net.module.util;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InetAddressUtilsTest {
+
+    private InetAddress parcelUnparcelAddress(InetAddress addr) {
+        Parcel p = Parcel.obtain();
+        InetAddressUtils.parcelInetAddress(p, addr, 0 /* flags */);
+        p.setDataPosition(0);
+        byte[] marshalled = p.marshall();
+        p.recycle();
+        p = Parcel.obtain();
+        p.unmarshall(marshalled, 0, marshalled.length);
+        p.setDataPosition(0);
+        InetAddress out = InetAddressUtils.unparcelInetAddress(p);
+        p.recycle();
+        return out;
+    }
+
+    @Test
+    public void testParcelUnparcelIpv4Address() throws Exception {
+        InetAddress ipv4 = InetAddress.getByName("192.0.2.1");
+        assertEquals(ipv4, parcelUnparcelAddress(ipv4));
+    }
+
+    @Test
+    public void testParcelUnparcelIpv6Address() throws Exception {
+        InetAddress ipv6 = InetAddress.getByName("2001:db8::1");
+        assertEquals(ipv6, parcelUnparcelAddress(ipv6));
+    }
+
+    @Test
+    public void testParcelUnparcelScopedIpv6Address() throws Exception {
+        InetAddress ipv6 = InetAddress.getByName("fe80::1%42");
+        assertEquals(42, ((Inet6Address) ipv6).getScopeId());
+        Inet6Address out = (Inet6Address) parcelUnparcelAddress(ipv6);
+        assertEquals(ipv6, out);
+        assertEquals(42, out.getScopeId());
+    }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/Ipv6UtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/Ipv6UtilsTest.java
new file mode 100644
index 0000000..37278b3
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/Ipv6UtilsTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 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 com.android.net.module.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.MacAddress;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.PrefixInformationOption;
+import com.android.net.module.util.structs.RaHeader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class Ipv6UtilsTest {
+
+    private static final MacAddress MAC1 = MacAddress.fromString("11:22:33:44:55:66");
+    private static final MacAddress MAC2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
+    private static final Inet6Address LINK_LOCAL = addr("fe80::1");
+    private static final Inet6Address ROUTER_LINK_LOCAL = addr("fe80::cafe:d00d");
+    private static final Inet6Address ALL_ROUTERS =
+            NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
+    private static final Inet6Address ALL_NODES =
+            NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
+
+    @Test
+    public void testBuildRsPacket() {
+        ByteBuffer b = Ipv6Utils.buildRsPacket(MAC1, MAC2, LINK_LOCAL, ALL_ROUTERS /* no opts */);
+
+        EthernetHeader eth = Struct.parse(EthernetHeader.class, b);
+        assertEquals(MAC1, eth.srcMac);
+        assertEquals(MAC2, eth.dstMac);
+
+        Ipv6Header ipv6 = Struct.parse(Ipv6Header.class, b);
+        assertEquals(255, ipv6.hopLimit);
+        assertEquals(OsConstants.IPPROTO_ICMPV6, ipv6.nextHeader);
+        assertEquals(LINK_LOCAL, ipv6.srcIp);
+        assertEquals(ALL_ROUTERS, ipv6.dstIp);
+
+        Icmpv6Header icmpv6 = Struct.parse(Icmpv6Header.class, b);
+        assertEquals(NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION, icmpv6.type);
+        assertEquals(0, icmpv6.code);
+    }
+
+    @Test
+    public void testBuildRaPacket() {
+        final byte pioFlags =
+                NetworkStackConstants.PIO_FLAG_AUTONOMOUS | NetworkStackConstants.PIO_FLAG_ON_LINK;
+        ByteBuffer pio1 = PrefixInformationOption.build(new IpPrefix("2001:db8:1::/64"),
+                pioFlags, 3600 /* validLifetime */, 1800 /* preferredLifetime */);
+        ByteBuffer pio2 = PrefixInformationOption.build(new IpPrefix("fdcd:a17f:6502:1::/64"),
+                pioFlags, 86400 /* validLifetime */, 86400 /* preferredLifetime */);
+
+        ByteBuffer b = Ipv6Utils.buildRaPacket(MAC2, MAC1, ROUTER_LINK_LOCAL, ALL_NODES,
+                (byte) 0 /* flags */, 7200 /* lifetime */,
+                30_000 /* reachableTime */, 750 /* retransTimer */,
+                pio1, pio2);
+
+        EthernetHeader eth = Struct.parse(EthernetHeader.class, b);
+        assertEquals(MAC2, eth.srcMac);
+        assertEquals(MAC1, eth.dstMac);
+
+        Ipv6Header ipv6 = Struct.parse(Ipv6Header.class, b);
+        assertEquals(255, ipv6.hopLimit);
+        assertEquals(OsConstants.IPPROTO_ICMPV6, ipv6.nextHeader);
+        assertEquals(ROUTER_LINK_LOCAL, ipv6.srcIp);
+        assertEquals(ALL_NODES, ipv6.dstIp);
+
+        Icmpv6Header icmpv6 = Struct.parse(Icmpv6Header.class, b);
+        assertEquals(NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT, icmpv6.type);
+        assertEquals(0, icmpv6.code);
+
+        RaHeader ra = Struct.parse(RaHeader.class, b);
+        assertEquals(0, ra.hopLimit);  // Hop limit: unspecified.
+        assertEquals(0, ra.flags);
+        assertEquals(7200, ra.lifetime);
+        assertEquals(30_000, ra.reachableTime);
+        assertEquals(750, ra.retransTimer);
+
+        PrefixInformationOption pio = Struct.parse(PrefixInformationOption.class, b);
+        assertPioEquals(pio, "2001:db8:1::/64", pioFlags, 3600, 1800);
+        pio = Struct.parse(PrefixInformationOption.class, b);
+        assertPioEquals(pio, "fdcd:a17f:6502:1::/64", pioFlags, 86400, 86400);
+    }
+
+    private void assertPioEquals(PrefixInformationOption pio, String prefix, byte flags,
+            long valid, long preferred) {
+        assertEquals(NetworkStackConstants.ICMPV6_ND_OPTION_PIO, pio.type);
+        assertEquals(4, pio.length);
+        assertEquals(flags, pio.flags);
+        assertEquals(valid, pio.validLifetime);
+        assertEquals(preferred, pio.preferredLifetime);
+        IpPrefix expected = new IpPrefix(prefix);
+        IpPrefix actual = new IpPrefix(pio.prefix, pio.prefixLen);
+        assertEquals(expected, actual);
+    }
+
+    private static Inet6Address addr(String addr) {
+        return (Inet6Address) InetAddresses.parseNumericAddress(addr);
+    }
+
+    private byte[] slice(byte[] array, int length) {
+        return Arrays.copyOf(array, length);
+    }
+}