Merge "Reintroduce android.test.base for Android R apps targeting Android Q."
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7748cf5..1c5ab84 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -994,6 +994,19 @@
 
     // Name of source package (for historical reasons, since BatteryStats tracked it).
     optional string package_name = 3;
+
+    // These enum values match the STANDBY_BUCKET_XXX constants defined in UsageStatsManager.java.
+    enum Bucket {
+        UNKNOWN = 0;
+        EXEMPTED = 5;
+        ACTIVE = 10;
+        WORKING_SET = 20;
+        FREQUENT = 30;
+        RARE = 40;
+        NEVER = 50;
+    }
+    // The App Standby bucket of the app that scheduled the alarm at the time the alarm fired.
+    optional Bucket app_standby_bucket = 4;
 }
 
 /**
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 68826cb..4b2b4c3 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -16,16 +16,17 @@
 
 package android.net;
 
+import static android.net.NetworkUtils.getDnsNetId;
 import static android.net.NetworkUtils.resNetworkCancel;
 import static android.net.NetworkUtils.resNetworkQuery;
 import static android.net.NetworkUtils.resNetworkResult;
 import static android.net.NetworkUtils.resNetworkSend;
+import static android.net.util.DnsUtils.haveIpv4;
+import static android.net.util.DnsUtils.haveIpv6;
+import static android.net.util.DnsUtils.rfc6724Sort;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.ENONET;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -34,18 +35,12 @@
 import android.os.CancellationSignal;
 import android.os.Looper;
 import android.system.ErrnoException;
-import android.system.Os;
 import android.util.Log;
 
-import libcore.io.IoUtils;
-
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
@@ -196,8 +191,8 @@
         final Object lock = new Object();
         final FileDescriptor queryfd;
         try {
-            queryfd = resNetworkSend((network != null
-                ? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags);
+            queryfd = resNetworkSend((network != null)
+                    ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
         } catch (ErrnoException e) {
             executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
             return;
@@ -237,8 +232,8 @@
         final Object lock = new Object();
         final FileDescriptor queryfd;
         try {
-            queryfd = resNetworkQuery((network != null
-                    ? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags);
+            queryfd = resNetworkQuery((network != null)
+                    ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
         } catch (ErrnoException e) {
             executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
             return;
@@ -252,14 +247,16 @@
 
     private class InetAddressAnswerAccumulator implements Callback<byte[]> {
         private final List<InetAddress> mAllAnswers;
+        private final Network mNetwork;
         private int mRcode;
         private DnsException mDnsException;
         private final Callback<? super List<InetAddress>> mUserCallback;
         private final int mTargetAnswerCount;
         private int mReceivedAnswerCount = 0;
 
-        InetAddressAnswerAccumulator(int size,
+        InetAddressAnswerAccumulator(@NonNull Network network, int size,
                 @NonNull Callback<? super List<InetAddress>> callback) {
+            mNetwork = network;
             mTargetAnswerCount = size;
             mAllAnswers = new ArrayList<>();
             mUserCallback = callback;
@@ -280,8 +277,7 @@
         private void maybeReportAnswer() {
             if (++mReceivedAnswerCount != mTargetAnswerCount) return;
             if (mAllAnswers.isEmpty() && maybeReportError()) return;
-            // TODO: Do RFC6724 sort.
-            mUserCallback.onAnswer(mAllAnswers, mRcode);
+            mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
         }
 
         @Override
@@ -308,7 +304,7 @@
 
     /**
      * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
-     * get back a set of InetAddresses asynchronously.
+     * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
      *
      * This method will examine the connection ability on given network, and query IPv4
      * and IPv6 if connection is available.
@@ -335,8 +331,23 @@
             return;
         }
         final Object lock = new Object();
-        final boolean queryIpv6 = haveIpv6(network);
-        final boolean queryIpv4 = haveIpv4(network);
+        final Network queryNetwork;
+        try {
+            queryNetwork = (network != null) ? network : new Network(getDnsNetId());
+        } catch (ErrnoException e) {
+            executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+            return;
+        }
+        final boolean queryIpv6 = haveIpv6(queryNetwork);
+        final boolean queryIpv4 = haveIpv4(queryNetwork);
+
+        // This can only happen if queryIpv4 and queryIpv6 are both false.
+        // This almost certainly means that queryNetwork does not exist or no longer exists.
+        if (!queryIpv6 && !queryIpv4) {
+            executor.execute(() -> callback.onError(
+                    new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
+            return;
+        }
 
         final FileDescriptor v4fd;
         final FileDescriptor v6fd;
@@ -345,9 +356,8 @@
 
         if (queryIpv6) {
             try {
-                v6fd = resNetworkQuery((network != null
-                        ? network.getNetIdForResolv() : NETID_UNSET),
-                        domain, CLASS_IN, TYPE_AAAA, flags);
+                v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
+                        TYPE_AAAA, flags);
             } catch (ErrnoException e) {
                 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
                 return;
@@ -355,7 +365,6 @@
             queryCount++;
         } else v6fd = null;
 
-        // TODO: Use device flag to control the sleep time.
         // Avoiding gateways drop packets if queries are sent too close together
         try {
             Thread.sleep(SLEEP_TIME_MS);
@@ -365,9 +374,8 @@
 
         if (queryIpv4) {
             try {
-                v4fd = resNetworkQuery((network != null
-                        ? network.getNetIdForResolv() : NETID_UNSET),
-                        domain, CLASS_IN, TYPE_A, flags);
+                v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
+                        flags);
             } catch (ErrnoException e) {
                 if (queryIpv6) resNetworkCancel(v6fd);  // Closes fd, marks it invalid.
                 executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
@@ -377,7 +385,7 @@
         } else v4fd = null;
 
         final InetAddressAnswerAccumulator accumulator =
-                new InetAddressAnswerAccumulator(queryCount, callback);
+                new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
 
         synchronized (lock)  {
             if (queryIpv6) {
@@ -398,7 +406,7 @@
 
     /**
      * Send a DNS query with the specified name and query type, get back a set of
-     * InetAddresses asynchronously.
+     * InetAddresses with rfc6724 sorting style asynchronously.
      *
      * The answer will be provided asynchronously through the provided {@link Callback}.
      *
@@ -423,15 +431,17 @@
         }
         final Object lock = new Object();
         final FileDescriptor queryfd;
+        final Network queryNetwork;
         try {
-            queryfd = resNetworkQuery((network != null
-                    ? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags);
+            queryNetwork = (network != null) ? network : new Network(getDnsNetId());
+            queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
+                    flags);
         } catch (ErrnoException e) {
             executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
             return;
         }
         final InetAddressAnswerAccumulator accumulator =
-                new InetAddressAnswerAccumulator(1, callback);
+                new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
         synchronized (lock)  {
             registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
             if (cancellationSignal == null) return;
@@ -500,38 +510,6 @@
         });
     }
 
-    // These two functions match the behaviour of have_ipv4 and have_ipv6 in the native resolver.
-    private boolean haveIpv4(@Nullable Network network) {
-        final SocketAddress addrIpv4 =
-                new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
-        return checkConnectivity(network, AF_INET, addrIpv4);
-    }
-
-    private boolean haveIpv6(@Nullable Network network) {
-        final SocketAddress addrIpv6 =
-                new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
-        return checkConnectivity(network, AF_INET6, addrIpv6);
-    }
-
-    private boolean checkConnectivity(@Nullable Network network,
-            int domain, @NonNull SocketAddress addr) {
-        final FileDescriptor socket;
-        try {
-            socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
-        } catch (ErrnoException e) {
-            return false;
-        }
-        try {
-            if (network != null) network.bindSocket(socket);
-            Os.connect(socket, addr);
-        } catch (IOException | ErrnoException e) {
-            return false;
-        } finally {
-            IoUtils.closeQuietly(socket);
-        }
-        return true;
-    }
-
     private static class DnsAddressAnswer extends DnsPacket {
         private static final String TAG = "DnsResolver.DnsAddressAnswer";
         private static final boolean DBG = false;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c06a132..a640f83 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -157,6 +157,13 @@
     public static native void resNetworkCancel(FileDescriptor fd);
 
     /**
+     * DNS resolver series jni method.
+     * Attempts to get netid of network which resolver will
+     * use if no network is explicitly selected.
+     */
+    public static native int getDnsNetId() throws ErrnoException;
+
+    /**
      * Get the tcp repair window associated with the {@code fd}.
      *
      * @param fd the tcp socket's {@link FileDescriptor}.
diff --git a/core/java/android/net/util/DnsUtils.java b/core/java/android/net/util/DnsUtils.java
new file mode 100644
index 0000000..e6abd50
--- /dev/null
+++ b/core/java/android/net/util/DnsUtils.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.Network;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.util.BitUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class DnsUtils {
+    private static final String TAG = "DnsUtils";
+    private static final int CHAR_BIT = 8;
+    public static final int IPV6_ADDR_SCOPE_NODELOCAL = 0x01;
+    public static final int IPV6_ADDR_SCOPE_LINKLOCAL = 0x02;
+    public static final int IPV6_ADDR_SCOPE_SITELOCAL = 0x05;
+    public static final int IPV6_ADDR_SCOPE_GLOBAL = 0x0e;
+    private static final Comparator<SortableAddress> sRfc6724Comparator = new Rfc6724Comparator();
+
+    /**
+     * Comparator to sort SortableAddress in Rfc6724 style.
+     */
+    public static class Rfc6724Comparator implements Comparator<SortableAddress> {
+        // This function matches the behaviour of _rfc6724_compare in the native resolver.
+        @Override
+        public int compare(SortableAddress span1, SortableAddress span2) {
+            // Rule 1: Avoid unusable destinations.
+            if (span1.hasSrcAddr != span2.hasSrcAddr) {
+                return span2.hasSrcAddr - span1.hasSrcAddr;
+            }
+
+            // Rule 2: Prefer matching scope.
+            if (span1.scopeMatch != span2.scopeMatch) {
+                return span2.scopeMatch - span1.scopeMatch;
+            }
+
+            // TODO: Implement rule 3: Avoid deprecated addresses.
+            // TODO: Implement rule 4: Prefer home addresses.
+
+            // Rule 5: Prefer matching label.
+            if (span1.labelMatch != span2.labelMatch) {
+                return span2.labelMatch - span1.labelMatch;
+            }
+
+            // Rule 6: Prefer higher precedence.
+            if (span1.precedence != span2.precedence) {
+                return span2.precedence - span1.precedence;
+            }
+
+            // TODO: Implement rule 7: Prefer native transport.
+
+            // Rule 8: Prefer smaller scope.
+            if (span1.scope != span2.scope) {
+                return span1.scope - span2.scope;
+            }
+
+            // Rule 9: Use longest matching prefix. IPv6 only.
+            if (span1.prefixMatchLen != span2.prefixMatchLen) {
+                return span2.prefixMatchLen - span1.prefixMatchLen;
+            }
+
+            // Rule 10: Leave the order unchanged. Collections.sort is a stable sort.
+            return 0;
+        }
+    }
+
+    /**
+     * Class used to sort with RFC 6724
+     */
+    public static class SortableAddress {
+        public final int label;
+        public final int labelMatch;
+        public final int scope;
+        public final int scopeMatch;
+        public final int precedence;
+        public final int prefixMatchLen;
+        public final int hasSrcAddr;
+        public final InetAddress address;
+
+        public SortableAddress(@NonNull InetAddress addr, @Nullable InetAddress srcAddr) {
+            address = addr;
+            hasSrcAddr = (srcAddr != null) ? 1 : 0;
+            label = findLabel(addr);
+            scope = findScope(addr);
+            precedence = findPrecedence(addr);
+            labelMatch = ((srcAddr != null) && (label == findLabel(srcAddr))) ? 1 : 0;
+            scopeMatch = ((srcAddr != null) && (scope == findScope(srcAddr))) ? 1 : 0;
+            if (isIpv6Address(addr) && isIpv6Address(srcAddr)) {
+                prefixMatchLen = compareIpv6PrefixMatchLen(srcAddr, addr);
+            } else {
+                prefixMatchLen = 0;
+            }
+        }
+    }
+
+    /**
+     * Sort the given address list in RFC6724 order.
+     * Will leave the list unchanged if an error occurs.
+     *
+     * This function matches the behaviour of _rfc6724_sort in the native resolver.
+     */
+    public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network,
+            @NonNull List<InetAddress> answers) {
+        List<SortableAddress> sortableAnswerList = new ArrayList<>();
+        answers.forEach(addr -> sortableAnswerList.add(
+                new SortableAddress(addr, findSrcAddress(network, addr))));
+
+        Collections.sort(sortableAnswerList, sRfc6724Comparator);
+
+        final List<InetAddress> sortedAnswers = new ArrayList<>();
+        sortableAnswerList.forEach(ans -> sortedAnswers.add(ans.address));
+
+        return sortedAnswers;
+    }
+
+    private static @Nullable InetAddress findSrcAddress(@Nullable Network network,
+            @NonNull InetAddress addr) {
+        final int domain;
+        if (isIpv4Address(addr)) {
+            domain = AF_INET;
+        } else if (isIpv6Address(addr)) {
+            domain = AF_INET6;
+        } else {
+            return null;
+        }
+        final FileDescriptor socket;
+        try {
+            socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "findSrcAddress:" + e.toString());
+            return null;
+        }
+        try {
+            if (network != null) network.bindSocket(socket);
+            Os.connect(socket, new InetSocketAddress(addr, 0));
+            return ((InetSocketAddress) Os.getsockname(socket)).getAddress();
+        } catch (IOException | ErrnoException e) {
+            return null;
+        } finally {
+            IoUtils.closeQuietly(socket);
+        }
+    }
+
+    /**
+     * Get the label for a given IPv4/IPv6 address.
+     * RFC 6724, section 2.1.
+     *
+     * Note that Java will return an IPv4-mapped address as an IPv4 address.
+     */
+    private static int findLabel(@NonNull InetAddress addr) {
+        if (isIpv4Address(addr)) {
+            return 4;
+        } else if (isIpv6Address(addr)) {
+            if (addr.isLoopbackAddress()) {
+                return 0;
+            } else if (isIpv6Address6To4(addr)) {
+                return 2;
+            } else if (isIpv6AddressTeredo(addr)) {
+                return 5;
+            } else if (isIpv6AddressULA(addr)) {
+                return 13;
+            } else if (((Inet6Address) addr).isIPv4CompatibleAddress()) {
+                return 3;
+            } else if (addr.isSiteLocalAddress()) {
+                return 11;
+            } else if (isIpv6Address6Bone(addr)) {
+                return 12;
+            } else {
+                // All other IPv6 addresses, including global unicast addresses.
+                return 1;
+            }
+        } else {
+            // This should never happen.
+            return 1;
+        }
+    }
+
+    private static boolean isIpv6Address(@Nullable InetAddress addr) {
+        return addr instanceof Inet6Address;
+    }
+
+    private static boolean isIpv4Address(@Nullable InetAddress addr) {
+        return addr instanceof Inet4Address;
+    }
+
+    private static boolean isIpv6Address6To4(@NonNull InetAddress addr) {
+        if (!isIpv6Address(addr)) return false;
+        final byte[] byteAddr = addr.getAddress();
+        return byteAddr[0] == 0x20 && byteAddr[1] == 0x02;
+    }
+
+    private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) {
+        if (!isIpv6Address(addr)) return false;
+        final byte[] byteAddr = addr.getAddress();
+        return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00
+                && byteAddr[3] == 0x00;
+    }
+
+    private static boolean isIpv6AddressULA(@NonNull InetAddress addr) {
+        return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc;
+    }
+
+    private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) {
+        if (!isIpv6Address(addr)) return false;
+        final byte[] byteAddr = addr.getAddress();
+        return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe;
+    }
+
+    private static int getIpv6MulticastScope(@NonNull InetAddress addr) {
+        return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f);
+    }
+
+    private static int findScope(@NonNull InetAddress addr) {
+        if (isIpv6Address(addr)) {
+            if (addr.isMulticastAddress()) {
+                return getIpv6MulticastScope(addr);
+            } else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
+                /**
+                 * RFC 4291 section 2.5.3 says loopback is to be treated as having
+                 * link-local scope.
+                 */
+                return IPV6_ADDR_SCOPE_LINKLOCAL;
+            } else if (addr.isSiteLocalAddress()) {
+                return IPV6_ADDR_SCOPE_SITELOCAL;
+            } else {
+                return IPV6_ADDR_SCOPE_GLOBAL;
+            }
+        } else if (isIpv4Address(addr)) {
+            if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
+                return IPV6_ADDR_SCOPE_LINKLOCAL;
+            } else {
+                /**
+                 * RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
+                 * and shared addresses (100.64.0.0/10), are assigned global scope.
+                 */
+                return IPV6_ADDR_SCOPE_GLOBAL;
+            }
+        } else {
+            /**
+             * This should never happen.
+             * Return a scope with low priority as a last resort.
+             */
+            return IPV6_ADDR_SCOPE_NODELOCAL;
+        }
+    }
+
+    /**
+     * Get the precedence for a given IPv4/IPv6 address.
+     * RFC 6724, section 2.1.
+     *
+     * Note that Java will return an IPv4-mapped address as an IPv4 address.
+     */
+    private static int findPrecedence(@NonNull InetAddress addr) {
+        if (isIpv4Address(addr)) {
+            return 35;
+        } else if (isIpv6Address(addr)) {
+            if (addr.isLoopbackAddress()) {
+                return 50;
+            } else if (isIpv6Address6To4(addr)) {
+                return 30;
+            } else if (isIpv6AddressTeredo(addr)) {
+                return 5;
+            } else if (isIpv6AddressULA(addr)) {
+                return 3;
+            } else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress()
+                    || isIpv6Address6Bone(addr)) {
+                return 1;
+            } else {
+                // All other IPv6 addresses, including global unicast addresses.
+                return 40;
+            }
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Find number of matching initial bits between the two addresses.
+     */
+    private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr,
+            @NonNull InetAddress dstAddr) {
+        final byte[] srcByte = srcAddr.getAddress();
+        final byte[] dstByte = dstAddr.getAddress();
+
+        // This should never happen.
+        if (srcByte.length != dstByte.length) return 0;
+
+        for (int i = 0; i < dstByte.length; ++i) {
+            if (srcByte[i] == dstByte[i]) {
+                continue;
+            }
+            int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]);
+            return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24);  // Java ints are 32 bits
+        }
+        return dstByte.length * CHAR_BIT;
+    }
+
+    /**
+     * Check if given network has Ipv4 capability
+     * This function matches the behaviour of have_ipv4 in the native resolver.
+     */
+    public static boolean haveIpv4(@Nullable Network network) {
+        final SocketAddress addrIpv4 =
+                new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
+        return checkConnectivity(network, AF_INET, addrIpv4);
+    }
+
+    /**
+     * Check if given network has Ipv6 capability
+     * This function matches the behaviour of have_ipv6 in the native resolver.
+     */
+    public static boolean haveIpv6(@Nullable Network network) {
+        final SocketAddress addrIpv6 =
+                new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
+        return checkConnectivity(network, AF_INET6, addrIpv6);
+    }
+
+    private static boolean checkConnectivity(@Nullable Network network,
+            int domain, @NonNull SocketAddress addr) {
+        final FileDescriptor socket;
+        try {
+            socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+        } catch (ErrnoException e) {
+            return false;
+        }
+        try {
+            if (network != null) network.bindSocket(socket);
+            Os.connect(socket, addr);
+        } catch (IOException | ErrnoException e) {
+            return false;
+        } finally {
+            IoUtils.closeQuietly(socket);
+        }
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/policy/PhoneLayoutInflater.java b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
index 0781f1a..991b6bb 100644
--- a/core/java/com/android/internal/policy/PhoneLayoutInflater.java
+++ b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
@@ -53,11 +53,6 @@
         call through to our super class.
     */
     @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
-        View fastView = fastCreateView(name, getContext(), attrs);
-        if (fastView != null) {
-            return fastView;
-        }
-
         for (String prefix : sClassPrefixList) {
             try {
                 View view = createView(name, prefix, attrs);
@@ -73,67 +68,6 @@
         return super.onCreateView(name, attrs);
     }
 
-    private static final View fastCreateView(String name, Context context, AttributeSet attrs) {
-        switch (name) {
-            case "ActionMenuView":
-                return new android.widget.ActionMenuView(context, attrs);
-            case "AutoCompleteTextView":
-                return new android.widget.AutoCompleteTextView(context, attrs);
-            case "Button":
-                return new android.widget.Button(context, attrs);
-            case "CheckBox":
-                return new android.widget.CheckBox(context, attrs);
-            case "CheckedTextView":
-                return new android.widget.CheckedTextView(context, attrs);
-            case "EditText":
-                return new android.widget.EditText(context, attrs);
-            case "FrameLayout":
-                return new android.widget.FrameLayout(context, attrs);
-            case "ImageButton":
-                return new android.widget.ImageButton(context, attrs);
-            case "ImageView":
-                return new android.widget.ImageView(context, attrs);
-            case "LinearLayout":
-                return new android.widget.LinearLayout(context, attrs);
-            case "ListView":
-                return new android.widget.ListView(context, attrs);
-            case "MultiAutoCompleteTextView":
-                return new android.widget.AutoCompleteTextView(context, attrs);
-            case "ProgressBar":
-                return new android.widget.ProgressBar(context, attrs);
-            case "RadioButton":
-                return new android.widget.RadioButton(context, attrs);
-            case "RatingBar":
-                return new android.widget.RatingBar(context, attrs);
-            case "RelativeLayout":
-                return new android.widget.RelativeLayout(context, attrs);
-            case "ScrollView":
-                return new android.widget.ScrollView(context, attrs);
-            case "SeekBar":
-                return new android.widget.SeekBar(context, attrs);
-            case "Space":
-                return new android.widget.Space(context, attrs);
-            case "Spinner":
-                return new android.widget.Spinner(context, attrs);
-            case "TextureView":
-                return new android.view.TextureView(context, attrs);
-            case "TextView":
-                return new android.widget.TextView(context, attrs);
-            case "ToggleButton":
-                return new android.widget.ToggleButton(context, attrs);
-            case "Toolbar":
-                return new android.widget.Toolbar(context, attrs);
-            case "View":
-                return new android.view.View(context, attrs);
-            case "ViewFlipper":
-                return new android.widget.ViewFlipper(context, attrs);
-            case "ViewStub":
-                return new android.view.ViewStub(context, attrs);
-            default:
-                return null;
-        }
-    }
-
     public LayoutInflater cloneInContext(Context newContext) {
         return new PhoneLayoutInflater(this, newContext);
     }
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index c5fc9b3..00e0e3a 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,26 +18,27 @@
 
 #include <vector>
 
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include "NetdClient.h"
-#include <utils/misc.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
 #include <arpa/inet.h>
-#include <net/if.h>
 #include <linux/filter.h>
 #include <linux/if_arp.h>
 #include <linux/tcp.h>
+#include <net/if.h>
 #include <netinet/ether.h>
 #include <netinet/icmp6.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
 #include <netinet/udp.h>
-#include <cutils/properties.h>
 
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/properties.h>
+#include <utils/misc.h>
+#include <utils/Log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "NetdClient.h"
 #include "core_jni_helpers.h"
+#include "jni.h"
 
 extern "C" {
 int ifc_enable(const char *ifname);
@@ -303,6 +304,15 @@
     jniSetFileDescriptorOfFD(env, javaFd, -1);
 }
 
+static jint android_net_utils_getDnsNetId(JNIEnv *env, jobject thiz) {
+    int dnsNetId = getNetworkForDns();
+    if (dnsNetId < 0) {
+        throwErrnoException(env, "getDnsNetId", -dnsNetId);
+    }
+
+    return dnsNetId;
+}
+
 static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
     if (javaFd == NULL) {
         jniThrowNullPointerException(env, NULL);
@@ -359,6 +369,7 @@
     { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
     { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
     { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
+    { "getDnsNetId", "()I", (void*) android_net_utils_getDnsNetId },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 551488a..841626c1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -35,7 +35,6 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
@@ -61,6 +60,7 @@
 
 import androidx.annotation.IntDef;
 import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
@@ -139,7 +139,7 @@
     @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
 
     private BubbleData mBubbleData;
-    private BubbleStackView mStackView;
+    @Nullable private BubbleStackView mStackView;
 
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index cb61259..dfbb55a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1698,8 +1698,6 @@
             return;
         }
 
-        type = fixTypeIfAuto(type);
-
         // Sanity check the window length.  This will catch people mistakenly
         // trying to pass an end-of-window timestamp rather than a duration.
         if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
@@ -1815,21 +1813,6 @@
     }
 
     /**
-     * In case of cars, we need to avoid scheduling wakeup alarms, since we don't want the system
-     * to wake up from suspend arbitrarily to perform app work.
-     */
-    private int fixTypeIfAuto(int type) {
-        if (mInjector.isAutomotive()) {
-            if (type == AlarmManager.ELAPSED_REALTIME_WAKEUP) {
-                type = AlarmManager.ELAPSED_REALTIME;
-            } else if (type == AlarmManager.RTC_WAKEUP) {
-                type = AlarmManager.RTC;
-            }
-        }
-        return type;
-    }
-
-    /**
      * Return the minimum time that should elapse before an app in the specified bucket
      * can receive alarms again
      */
@@ -2232,7 +2215,6 @@
             pw.print("  mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
             pw.print("  mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
             pw.print("  mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
-            pw.print("  mIsAutomotive="); pw.println(mInjector.isAutomotive());
 
             if (RECORD_ALARMS_IN_HISTORY) {
                 pw.println();
@@ -3899,12 +3881,9 @@
     static class Injector {
         private long mNativeData;
         private Context mContext;
-        private final boolean mIsAutomotive;
 
         Injector(Context context) {
             mContext = context;
-            mIsAutomotive = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE);
         }
 
         void init() {
@@ -3993,10 +3972,6 @@
         ClockReceiver getClockReceiver(AlarmManagerService service) {
             return service.new ClockReceiver();
         }
-
-        boolean isAutomotive() {
-            return mIsAutomotive;
-        }
     }
 
     private class AlarmThread extends Thread
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf98f6c..e1914f5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8442,18 +8442,43 @@
             }
         }
 
-        if (DEBUG_POWER) {
-            Slog.w(TAG, "noteWakupAlarm[ sourcePkg=" + sourcePkg + ", sourceUid=" + sourceUid
-                    + ", workSource=" + workSource + ", tag=" + tag + "]");
-        }
+        int standbyBucket = 0;
 
         mBatteryStatsService.noteWakupAlarm(sourcePkg, sourceUid, workSource, tag);
         if (workSource != null) {
-            StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag, sourcePkg);
+            String workSourcePackage = workSource.getName(0);
+            int workSourceUid = workSource.getAttributionUid();
+            if (workSourcePackage == null) {
+                workSourcePackage = sourcePkg;
+                workSourceUid = sourceUid;
+            }
+
+            if (mUsageStatsService != null) {
+                standbyBucket = mUsageStatsService.getAppStandbyBucket(workSourcePackage,
+                        UserHandle.getUserId(workSourceUid), SystemClock.elapsedRealtime());
+            }
+
+            StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag, sourcePkg,
+                    standbyBucket);
+            if (DEBUG_POWER) {
+                Slog.w(TAG, "noteWakeupAlarm[ sourcePkg=" + sourcePkg + ", sourceUid=" + sourceUid
+                        + ", workSource=" + workSource + ", tag=" + tag + ", standbyBucket="
+                        + standbyBucket + " wsName=" + workSourcePackage + ")]");
+            }
         } else {
+            if (mUsageStatsService != null) {
+                standbyBucket = mUsageStatsService.getAppStandbyBucket(sourcePkg,
+                        UserHandle.getUserId(sourceUid), SystemClock.elapsedRealtime());
+            }
             StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, tag,
-                    sourcePkg);
+                    sourcePkg, standbyBucket);
+            if (DEBUG_POWER) {
+                Slog.w(TAG, "noteWakeupAlarm[ sourcePkg=" + sourcePkg + ", sourceUid=" + sourceUid
+                        + ", workSource=" + workSource + ", tag=" + tag + ", standbyBucket="
+                        + standbyBucket + "]");
+            }
         }
+
     }
 
     @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index 5acf83a..a9662c9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -85,6 +85,10 @@
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
                 InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        // TODO(b/133396459): replace with a constant.
+        map.put(
+                26 /* kDownloadMetadataSignatureMismatch */,
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
 
         // Error constants corresponding to errors related to devices bad state.
         map.put(
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 77e2517..cdcdf91 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -68,7 +68,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -122,8 +121,6 @@
     private AlarmManagerService.ClockReceiver mClockReceiver;
     @Mock
     private PowerManager.WakeLock mWakeLock;
-    @Mock
-    private PackageManager mMockPackageManager;
 
     private MockitoSession mMockingSession;
     private Injector mInjector;
@@ -241,11 +238,6 @@
         PowerManager.WakeLock getAlarmWakeLock() {
             return mWakeLock;
         }
-
-        @Override
-        boolean isAutomotive() {
-            return mIsAutomotiveOverride;
-        }
     }
 
     @Before
@@ -272,7 +264,6 @@
         when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
         doReturn("min_futurity=0,min_interval=0").when(() ->
                 Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
-        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
 
         mInjector = new Injector(mMockContext);
         mService = new AlarmManagerService(mMockContext, mInjector);
@@ -1024,18 +1015,6 @@
         assertAlarmTypeConversion(typesToSet, typesExpected);
     }
 
-    /**
-     * Confirm that wakeup alarms are never set for automotive.
-     */
-    @Test
-    public void alarmTypesForAuto() throws Exception {
-        mInjector.mIsAutomotiveOverride = true;
-        final int[] typesToSet = {ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME, RTC_WAKEUP, RTC};
-        final int[] typesExpected = {ELAPSED_REALTIME, ELAPSED_REALTIME, ELAPSED_REALTIME,
-                ELAPSED_REALTIME};
-        assertAlarmTypeConversion(typesToSet, typesExpected);
-    }
-
     private void assertAlarmTypeConversion(int[] typesToSet, int[] typesExpected) throws Exception {
         for (int i = 0; i < typesToSet.length; i++) {
             setTestAlarm(typesToSet[i], 1234, getNewMockPendingIntent());
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 3b11741..6860235 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1962,16 +1962,22 @@
     }
 
     /**
-     * Get default sms subscription id
+     * Get default sms subscription id.
      *
-     * @return the user-defined default SMS subscription id or
-     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if no default is set.
+     * <p class="note"><strong>Note:</strong>This returns a value different from
+     * {@link SubscriptionManager#getDefaultSmsSubscriptionId} if the user has not chosen a default.
+     * In this case it returns the active subscription id if there's only one active subscription
+     * available.
+     *
+     * @return the user-defined default SMS subscription id, or the active subscription id if
+     * there's only one active subscription available, otherwise
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
      */
     public static int getDefaultSmsSubscriptionId() {
         try {
-            return SubscriptionManager.getDefaultSmsSubscriptionId();
-        } catch (NullPointerException ex) {
-            return -1;
+            return getISmsServiceOrThrow().getPreferredSmsSubscription();
+        } catch (RemoteException e) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
     }
 
diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/tests/net/java/android/net/util/DnsUtilsTest.java
new file mode 100644
index 0000000..e5cb09f
--- /dev/null
+++ b/tests/net/java/android/net/util/DnsUtilsTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_GLOBAL;
+import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_LINKLOCAL;
+import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_SITELOCAL;
+import static android.net.util.DnsUtils.rfc6724Sort;
+
+import static org.junit.Assert.assertEquals;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsUtilsTest {
+    private InetAddress stringToAddress(@NonNull String addr) {
+        return InetAddresses.parseNumericAddress(addr);
+    }
+
+    private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr) {
+        return makeSortableAddress(addr, null);
+    }
+
+    private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr,
+            @Nullable String srcAddr) {
+        return new DnsUtils.SortableAddress(stringToAddress(addr),
+                srcAddr != null ? stringToAddress(srcAddr) : null);
+    }
+
+    @Test
+    public void testRfc6724Sort() {
+        final List<InetAddress> testAddresses = Arrays.asList(
+                stringToAddress("172.217.24.14"),
+                stringToAddress("216.58.200.46"),
+                stringToAddress("2404:6800:4008:802::200e"));
+
+        final List<InetAddress> expected = Arrays.asList(
+                stringToAddress("2404:6800:4008:802::200e"),
+                stringToAddress("172.217.24.14"),
+                stringToAddress("216.58.200.46"));
+
+        final List<InetAddress> result = rfc6724Sort(null, testAddresses);
+
+        assertEquals(result.size(), testAddresses.size());
+        assertEquals(result, expected);
+    }
+
+    @Test
+    public void testRfc6724Comparator() {
+        final List<DnsUtils.SortableAddress> test = Arrays.asList(
+                makeSortableAddress("216.58.200.36"),             // Ipv4
+                makeSortableAddress("2404:6800:4008:801::2004"),  // global
+                makeSortableAddress("::1"),                       // loop back
+                makeSortableAddress("fe80::c46f:1cff:fe04:39b4"), // link local
+                makeSortableAddress("::ffff:192.168.95.3"),       // IPv4-mapped IPv6
+                makeSortableAddress("2001::47c1"),                // teredo tunneling
+                makeSortableAddress("::216.58.200.36"),           // IPv4-compatible
+                makeSortableAddress("3ffe::1234:5678"));          // 6bone
+
+        final List<InetAddress> expected = Arrays.asList(
+                stringToAddress("::1"),                       // loop back
+                stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local
+                stringToAddress("2404:6800:4008:801::2004"),  // global
+                stringToAddress("216.58.200.36"),             // Ipv4
+                stringToAddress("::ffff:192.168.95.3"),       // IPv4-mapped IPv6
+                stringToAddress("2001::47c1"),                // teredo tunneling
+                stringToAddress("::216.58.200.36"),            // IPv4-compatible
+                stringToAddress("3ffe::1234:5678"));          // 6bone
+
+        Collections.sort(test, new DnsUtils.Rfc6724Comparator());
+
+        for (int i = 0; i < test.size(); ++i) {
+            assertEquals(test.get(i).address, expected.get(i));
+        }
+
+        // TODO: add more combinations
+    }
+
+    @Test
+    public void testV4SortableAddress() {
+        // Test V4 address
+        DnsUtils.SortableAddress test = makeSortableAddress("216.58.200.36");
+        assertEquals(test.hasSrcAddr, 0);
+        assertEquals(test.prefixMatchLen, 0);
+        assertEquals(test.address, stringToAddress("216.58.200.36"));
+        assertEquals(test.labelMatch, 0);
+        assertEquals(test.scopeMatch, 0);
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 4);
+        assertEquals(test.precedence, 35);
+
+        // Test V4 loopback address with the same source address
+        test = makeSortableAddress("127.1.2.3", "127.1.2.3");
+        assertEquals(test.hasSrcAddr, 1);
+        assertEquals(test.prefixMatchLen, 0);
+        assertEquals(test.address, stringToAddress("127.1.2.3"));
+        assertEquals(test.labelMatch, 1);
+        assertEquals(test.scopeMatch, 1);
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL);
+        assertEquals(test.label, 4);
+        assertEquals(test.precedence, 35);
+    }
+
+    @Test
+    public void testV6SortableAddress() {
+        // Test global address
+        DnsUtils.SortableAddress test = makeSortableAddress("2404:6800:4008:801::2004");
+        assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004"));
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 1);
+        assertEquals(test.precedence, 40);
+
+        // Test global address with global source address
+        test = makeSortableAddress("2404:6800:4008:801::2004",
+                "2401:fa00:fc:fd00:6d6c:7199:b8e7:41d6");
+        assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004"));
+        assertEquals(test.hasSrcAddr, 1);
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.labelMatch, 1);
+        assertEquals(test.scopeMatch, 1);
+        assertEquals(test.label, 1);
+        assertEquals(test.precedence, 40);
+        assertEquals(test.prefixMatchLen, 13);
+
+        // Test global address with linklocal source address
+        test = makeSortableAddress("2404:6800:4008:801::2004", "fe80::c46f:1cff:fe04:39b4");
+        assertEquals(test.hasSrcAddr, 1);
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.labelMatch, 1);
+        assertEquals(test.scopeMatch, 0);
+        assertEquals(test.label, 1);
+        assertEquals(test.precedence, 40);
+        assertEquals(test.prefixMatchLen, 0);
+
+        // Test loopback address with the same source address
+        test = makeSortableAddress("::1", "::1");
+        assertEquals(test.hasSrcAddr, 1);
+        assertEquals(test.prefixMatchLen, 16 * 8);
+        assertEquals(test.labelMatch, 1);
+        assertEquals(test.scopeMatch, 1);
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL);
+        assertEquals(test.label, 0);
+        assertEquals(test.precedence, 50);
+
+        // Test linklocal address
+        test = makeSortableAddress("fe80::c46f:1cff:fe04:39b4");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL);
+        assertEquals(test.label, 1);
+        assertEquals(test.precedence, 40);
+
+        // Test linklocal address
+        test = makeSortableAddress("fe80::");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL);
+        assertEquals(test.label, 1);
+        assertEquals(test.precedence, 40);
+
+        // Test 6to4 address
+        test = makeSortableAddress("2002:c000:0204::");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 2);
+        assertEquals(test.precedence, 30);
+
+        // Test unique local address
+        test = makeSortableAddress("fc00::c000:13ab");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 13);
+        assertEquals(test.precedence, 3);
+
+        // Test teredo tunneling address
+        test = makeSortableAddress("2001::47c1");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 5);
+        assertEquals(test.precedence, 5);
+
+        // Test IPv4-compatible addresses
+        test = makeSortableAddress("::216.58.200.36");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 3);
+        assertEquals(test.precedence, 1);
+
+        // Test site-local address
+        test = makeSortableAddress("fec0::cafe:3ab2");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_SITELOCAL);
+        assertEquals(test.label, 11);
+        assertEquals(test.precedence, 1);
+
+        // Test 6bone address
+        test = makeSortableAddress("3ffe::1234:5678");
+        assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL);
+        assertEquals(test.label, 12);
+        assertEquals(test.precedence, 1);
+    }
+}