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);
+ }
+}