Snap for 10040347 from 9f31270334e28a3bdc0a577e5e3541417f5ae5fd to mainline-mediaprovider-release

Change-Id: If025e218acb61b153ea524b9483990a13e3299f6
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index 71632af..f1fb3da 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -46,6 +46,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.HexDump;
+import com.android.net.module.util.InetAddressUtils;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.SharedLog;
 import com.android.net.module.util.ip.NetlinkMonitor;
@@ -65,7 +66,6 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -540,17 +540,8 @@
             if (!mNetlinkEventParsingEnabled) return;
             final String[] addresses = new String[opt.servers.length];
             for (int i = 0; i < opt.servers.length; i++) {
-                Inet6Address addr = (Inet6Address) opt.servers[i];
-                if (addr.isLinkLocalAddress()) {
-                    try {
-                        addr = Inet6Address.getByAddress(null /* hostname */, addr.getAddress(),
-                                mIfindex);
-                    } catch (UnknownHostException impossible) {
-                        Log.wtf("Cannot construct scoped Inet6Address with Inet6Address.getAddress("
-                                + addr.getHostAddress() + "): ", impossible);
-                    }
-                }
-                addresses[i] = addr.getHostAddress();
+                final Inet6Address addr = opt.servers[i];
+                addresses[i] = InetAddressUtils.withScopeId(addr, mIfindex).getHostAddress();
             }
             updateInterfaceDnsServerInfo(opt.header.lifetime, addresses);
         }
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java
index 0b3bba7..285fcb4 100644
--- a/tests/unit/src/android/net/apf/ApfTest.java
+++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -24,6 +24,7 @@
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_IPV6;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_STREAM;
@@ -62,8 +63,10 @@
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -101,7 +104,9 @@
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Random;
 
@@ -1357,8 +1362,111 @@
         buf.putShort(pointer);
     }
 
-    private static byte[] makeMdnsCompressedV6Packet() throws IOException {
-        ByteBuffer buf = ByteBuffer.wrap(new byte[256]);
+
+    // Simplistic DNS compression code that intentionally does not depend on production code.
+    private static List<Pair<Integer, String>> getDnsLabels(int startOffset, String... names) {
+        // Maps all possible name suffixes to packet offsets.
+        final HashMap<String, Integer> mPointerOffsets = new HashMap<>();
+        final List<Pair<Integer, String>> out = new ArrayList<>();
+        int offset = startOffset;
+        for (int i = 0; i < names.length; i++) {
+            String name = names[i];
+            while (true) {
+                if (name.length() == 0) {
+                    out.add(label(""));
+                    offset += 1 + 4;  // 1-byte label, DNS query
+                    break;
+                }
+
+                final int pointerOffset = mPointerOffsets.getOrDefault(name, -1);
+                if (pointerOffset != -1) {
+                    out.add(pointer(pointerOffset));
+                    offset += 2 + 4; // 2-byte pointer, DNS query
+                    break;
+                }
+
+                mPointerOffsets.put(name, offset);
+
+                final int indexOfDot = name.indexOf(".");
+                final String label;
+                if (indexOfDot == -1) {
+                    label = name;
+                    name = "";
+                } else {
+                    label = name.substring(0, indexOfDot);
+                    name = name.substring(indexOfDot + 1);
+                }
+                out.add(label(label));
+                offset += 1 + label.length();
+            }
+        }
+        return out;
+    }
+
+    static Pair<Integer, String> label(String label) {
+        return Pair.create(label.length(), label);
+    }
+
+    static Pair<Integer, String> pointer(int offset) {
+        return Pair.create(0xc000 | offset, null);
+    }
+
+    @Test
+    public void testGetDnsLabels() throws Exception {
+        int startOffset = 12;
+        List<Pair<Integer, String>> actual = getDnsLabels(startOffset, "myservice.tcp.local");
+        assertEquals(4, actual.size());
+        assertEquals(label("myservice"), actual.get(0));
+        assertEquals(label("tcp"), actual.get(1));
+        assertEquals(label("local"), actual.get(2));
+        assertEquals(label(""), actual.get(3));
+
+        startOffset = 30;
+        actual = getDnsLabels(startOffset,
+                "myservice.tcp.local", "foo.tcp.local", "myhostname.local", "bar.udp.local",
+                "foo.myhostname.local");
+        final int tcpLocalOffset = startOffset + 1 + "myservice".length();
+        final int localOffset = startOffset + 1 + "myservice".length() + 1 + "tcp".length();
+        final int myhostnameLocalOffset = 30
+                + 1 + "myservice".length() + 1 + "tcp".length() + 1 + "local".length() + 1 + 4
+                + 1 + "foo".length() + 2 + 4;
+
+        assertEquals(13, actual.size());
+        assertEquals(label("myservice"), actual.get(0));
+        assertEquals(label("tcp"), actual.get(1));
+        assertEquals(label("local"), actual.get(2));
+        assertEquals(label(""), actual.get(3));
+        assertEquals(label("foo"), actual.get(4));
+        assertEquals(pointer(tcpLocalOffset), actual.get(5));
+        assertEquals(label("myhostname"), actual.get(6));
+        assertEquals(pointer(localOffset), actual.get(7));
+        assertEquals(label("bar"), actual.get(8));
+        assertEquals(label("udp"), actual.get(9));
+        assertEquals(pointer(localOffset), actual.get(10));
+        assertEquals(label("foo"), actual.get(11));
+        assertEquals(pointer(myhostnameLocalOffset), actual.get(12));
+
+    }
+
+    private static byte[] makeMdnsCompressedV6Packet(String... names) throws IOException {
+        ByteBuffer questions = ByteBuffer.allocate(1500);
+        questions.put(new DnsPacket.DnsHeader(123, 0, names.length, 0).getBytes());
+        final List<Pair<Integer, String>> labels = getDnsLabels(questions.position(), names);
+        for (Pair<Integer, String> label : labels) {
+            final String name = label.second;
+            if (name == null) {
+                putPointer(questions, label.first);
+            } else {
+                putLabel(questions, name);
+            }
+            if (TextUtils.isEmpty(name)) {
+                questions.put(new byte[4]);
+            }
+        }
+        questions.flip();
+
+        ByteBuffer buf = PacketBuilder.allocate(/*hasEther=*/ true, IPPROTO_IPV6, IPPROTO_UDP,
+                questions.limit());
         final PacketBuilder builder = new PacketBuilder(buf);
         builder.writeL2Header(MacAddress.fromString("11:22:33:44:55:66"),
                 MacAddress.fromBytes(ETH_MULTICAST_MDNS_V6_MAC_ADDRESS),
@@ -1368,38 +1476,23 @@
                 (Inet6Address) Inet6Address.getByAddress(IPV6_MDNS_MULTICAST_ADDR));
         builder.writeUdpHeader((short) MDNS_UDP_PORT, (short) MDNS_UDP_PORT);
 
-        ByteBuffer questions = ByteBuffer.allocate(128);
-        questions.put(new DnsPacket.DnsHeader(123, 0, 4, 0).getBytes());
-
-        // myservice.tcp.local
-        putLabel(questions, "myservice");
-        final int offsetTcpLocal = questions.position();
-        putLabel(questions, "tcp");
-        final int offsetLocal = questions.position();
-        putLabel(questions, "local");
-        putLabel(questions, "");
-        questions.put(new byte[4]);
-
-        // googlecast.tcp.local
-        putLabel(questions, "googlecast");
-        putPointer(questions, offsetTcpLocal);
-        questions.put(new byte[4]);
-
-        // matter.tcp.local
-        putLabel(questions, "matter");
-        putPointer(questions, offsetTcpLocal);
-        questions.put(new byte[4]);
-
-        // myhostname.local
-        putLabel(questions, "myhostname");
-        putPointer(questions, offsetLocal);
-        questions.put(new byte[4]);
-
-        buf.put(questions.array());
+        buf.put(questions);
 
         return builder.finalizePacket().array();
     }
 
+    private static byte[] makeMdnsCompressedV6Packet() throws IOException {
+        return makeMdnsCompressedV6Packet("myservice.tcp.local", "googlecast.tcp.local",
+                "matter.tcp.local", "myhostname.local");
+    }
+
+    private static byte[] makeMdnsCompressedV6PacketWithManyNames() throws IOException {
+        return makeMdnsCompressedV6Packet("myservice.tcp.local", "googlecast.tcp.local",
+                "matter.tcp.local", "myhostname.local", "myhostname2.local", "myhostname3.local",
+                "myhostname4.local", "myhostname5.local", "myhostname6.local", "myhostname7.local");
+
+    }
+
     /** Adds to the program a no-op instruction that is one byte long. */
     private void addOneByteNoop(ApfGenerator gen) {
         gen.addOr(0);
@@ -1649,6 +1742,8 @@
         doTestDnsParsingNecessaryOverhead(0, "foo.tcp.local",
                 makeMdnsCompressedV6Packet(), "compressed packet");
 
+        doTestDnsParsingNecessaryOverhead(235, "foo.tcp.local",
+                makeMdnsCompressedV6PacketWithManyNames(), "compressed packet with many names");
     }
 
     @Test