Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -27231,6 +27231,7 @@ package android.net { method @NonNull public static android.net.DnsResolver getInstance(); method public <T> void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback<T>); method public <T> void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback<T>); method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.InetAddressAnswerCallback); field public static final int CLASS_IN = 1; // 0x1 field public static final int FLAG_EMPTY = 0; // 0x0 field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 core/java/android/net/DnsResolver.java +194 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,10 @@ import static android.net.NetworkUtils.resNetworkResult; import static android.net.NetworkUtils.resNetworkSend; 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 android.annotation.CallbackExecutor; import android.annotation.IntDef; Loading @@ -30,12 +34,18 @@ import android.annotation.Nullable; 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; Loading @@ -52,6 +62,7 @@ public final class DnsResolver { private static final String TAG = "DnsResolver"; private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; private static final int MAXPACKET = 8 * 1024; private static final int SLEEP_TIME = 2; @IntDef(prefix = { "CLASS_" }, value = { CLASS_IN Loading Loading @@ -188,9 +199,9 @@ public final class DnsResolver { * Send a raw DNS query. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param query blob message * @param query blob message to query * @param flags flags as a combination of the FLAGS_* constants * @param executor The {@link Executor} that the callback should be executed on. * @param cancellationSignal used by the caller to signal if the query should be Loading @@ -211,21 +222,23 @@ public final class DnsResolver { queryfd = resNetworkSend((network != null ? network.netId : NETID_UNSET), query, query.length, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } maybeAddCancellationSignal(cancellationSignal, queryfd, lock); registerFDListener(executor, queryfd, callback, cancellationSignal, lock); maybeAddCancellationSignal(cancellationSignal, queryfd, lock); } /** * Send a DNS query with the specified name, class and query type. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param domain domain name for querying * @param domain domain name to query * @param nsClass dns class as one of the CLASS_* constants * @param nsType dns resource record (RR) type as one of the TYPE_* constants * @param flags flags as a combination of the FLAGS_* constants Loading @@ -249,12 +262,145 @@ public final class DnsResolver { queryfd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } maybeAddCancellationSignal(cancellationSignal, queryfd, lock); registerFDListener(executor, queryfd, callback, cancellationSignal, lock); maybeAddCancellationSignal(cancellationSignal, queryfd, lock); } private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback { private final List<InetAddress> mAllAnswers; private ParseException mParseException; private ErrnoException mErrnoException; private final InetAddressAnswerCallback mUserCallback; private final int mTargetAnswerCount; private int mReceivedAnswerCount = 0; InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerCallback callback) { mTargetAnswerCount = size; mAllAnswers = new ArrayList<>(); mUserCallback = callback; } private boolean maybeReportException() { if (mErrnoException != null) { mUserCallback.onQueryException(mErrnoException); return true; } if (mParseException != null) { mUserCallback.onParseException(mParseException); return true; } return false; } private void maybeReportAnswer() { if (++mReceivedAnswerCount != mTargetAnswerCount) return; if (mAllAnswers.isEmpty() && maybeReportException()) return; // TODO: Do RFC6724 sort. mUserCallback.onAnswer(mAllAnswers); } @Override public void onAnswer(@NonNull List<InetAddress> answer) { mAllAnswers.addAll(answer); maybeReportAnswer(); } @Override public void onParseException(@NonNull ParseException e) { mParseException = e; maybeReportAnswer(); } @Override public void onQueryException(@NonNull ErrnoException e) { mErrnoException = e; maybeReportAnswer(); } } /** * Send a DNS query with the specified name, get back a set of InetAddresses asynchronously. * The answer will be provided asynchronously through the provided * {@link InetAddressAnswerCallback}. * * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param domain domain name to query * @param flags flags as a combination of the FLAGS_* constants * @param executor The {@link Executor} that the callback should be executed on. * @param cancellationSignal used by the caller to signal if the query should be * cancelled. May be {@code null}. * @param callback an {@link InetAddressAnswerCallback} which will be called to notify the * caller of the result of dns query. */ public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags, @NonNull @CallbackExecutor Executor executor, @Nullable CancellationSignal cancellationSignal, @NonNull InetAddressAnswerCallback callback) { if (cancellationSignal != null && cancellationSignal.isCanceled()) { return; } final Object lock = new Object(); final boolean queryIpv6 = haveIpv6(network); final boolean queryIpv4 = haveIpv4(network); final FileDescriptor v4fd; final FileDescriptor v6fd; int queryCount = 0; if (queryIpv6) { try { v6fd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } queryCount++; } else v6fd = null; // TODO: Use device flag to controll the sleep time. // Avoiding gateways drop packets if queries are sent too close together try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException ex) { } if (queryIpv4) { try { v4fd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); } catch (ErrnoException e) { if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. executor.execute(() -> { callback.onQueryException(e); }); return; } queryCount++; } else v4fd = null; final InetAddressAnswerAccumulator accumulator = new InetAddressAnswerAccumulator(queryCount, callback); if (queryIpv6) registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock); if (queryIpv4) registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock); if (cancellationSignal == null) return; cancellationSignal.setOnCancelListener(() -> { synchronized (lock) { if (queryIpv4) cancelQuery(v4fd); if (queryIpv6) cancelQuery(v6fd); } }); } private <T> void registerFDListener(@NonNull Executor executor, Loading @@ -271,7 +417,7 @@ public final class DnsResolver { } byte[] answerbuf = null; try { answerbuf = resNetworkResult(fd); answerbuf = resNetworkResult(fd); // Closes fd, marks it invalid. } catch (ErrnoException e) { Log.e(TAG, "resNetworkResult:" + e.toString()); answerCallback.onQueryException(e); Loading @@ -291,19 +437,54 @@ public final class DnsResolver { }); } private void cancelQuery(@NonNull FileDescriptor queryfd) { if (!queryfd.valid()) return; Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd); resNetworkCancel(queryfd); // Closes fd, marks it invalid. } private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal, @NonNull FileDescriptor queryfd, @NonNull Object lock) { if (cancellationSignal == null) return; cancellationSignal.setOnCancelListener(() -> { synchronized (lock) { if (!queryfd.valid()) return; Looper.getMainLooper().getQueue() .removeOnFileDescriptorEventListener(queryfd); resNetworkCancel(queryfd); cancelQuery(queryfd); } }); } // 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; Loading Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -27231,6 +27231,7 @@ package android.net { method @NonNull public static android.net.DnsResolver getInstance(); method public <T> void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback<T>); method public <T> void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback<T>); method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.InetAddressAnswerCallback); field public static final int CLASS_IN = 1; // 0x1 field public static final int FLAG_EMPTY = 0; // 0x0 field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
core/java/android/net/DnsResolver.java +194 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,10 @@ import static android.net.NetworkUtils.resNetworkResult; import static android.net.NetworkUtils.resNetworkSend; 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 android.annotation.CallbackExecutor; import android.annotation.IntDef; Loading @@ -30,12 +34,18 @@ import android.annotation.Nullable; 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; Loading @@ -52,6 +62,7 @@ public final class DnsResolver { private static final String TAG = "DnsResolver"; private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; private static final int MAXPACKET = 8 * 1024; private static final int SLEEP_TIME = 2; @IntDef(prefix = { "CLASS_" }, value = { CLASS_IN Loading Loading @@ -188,9 +199,9 @@ public final class DnsResolver { * Send a raw DNS query. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param query blob message * @param query blob message to query * @param flags flags as a combination of the FLAGS_* constants * @param executor The {@link Executor} that the callback should be executed on. * @param cancellationSignal used by the caller to signal if the query should be Loading @@ -211,21 +222,23 @@ public final class DnsResolver { queryfd = resNetworkSend((network != null ? network.netId : NETID_UNSET), query, query.length, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } maybeAddCancellationSignal(cancellationSignal, queryfd, lock); registerFDListener(executor, queryfd, callback, cancellationSignal, lock); maybeAddCancellationSignal(cancellationSignal, queryfd, lock); } /** * Send a DNS query with the specified name, class and query type. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param domain domain name for querying * @param domain domain name to query * @param nsClass dns class as one of the CLASS_* constants * @param nsType dns resource record (RR) type as one of the TYPE_* constants * @param flags flags as a combination of the FLAGS_* constants Loading @@ -249,12 +262,145 @@ public final class DnsResolver { queryfd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } maybeAddCancellationSignal(cancellationSignal, queryfd, lock); registerFDListener(executor, queryfd, callback, cancellationSignal, lock); maybeAddCancellationSignal(cancellationSignal, queryfd, lock); } private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback { private final List<InetAddress> mAllAnswers; private ParseException mParseException; private ErrnoException mErrnoException; private final InetAddressAnswerCallback mUserCallback; private final int mTargetAnswerCount; private int mReceivedAnswerCount = 0; InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerCallback callback) { mTargetAnswerCount = size; mAllAnswers = new ArrayList<>(); mUserCallback = callback; } private boolean maybeReportException() { if (mErrnoException != null) { mUserCallback.onQueryException(mErrnoException); return true; } if (mParseException != null) { mUserCallback.onParseException(mParseException); return true; } return false; } private void maybeReportAnswer() { if (++mReceivedAnswerCount != mTargetAnswerCount) return; if (mAllAnswers.isEmpty() && maybeReportException()) return; // TODO: Do RFC6724 sort. mUserCallback.onAnswer(mAllAnswers); } @Override public void onAnswer(@NonNull List<InetAddress> answer) { mAllAnswers.addAll(answer); maybeReportAnswer(); } @Override public void onParseException(@NonNull ParseException e) { mParseException = e; maybeReportAnswer(); } @Override public void onQueryException(@NonNull ErrnoException e) { mErrnoException = e; maybeReportAnswer(); } } /** * Send a DNS query with the specified name, get back a set of InetAddresses asynchronously. * The answer will be provided asynchronously through the provided * {@link InetAddressAnswerCallback}. * * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. * @param domain domain name to query * @param flags flags as a combination of the FLAGS_* constants * @param executor The {@link Executor} that the callback should be executed on. * @param cancellationSignal used by the caller to signal if the query should be * cancelled. May be {@code null}. * @param callback an {@link InetAddressAnswerCallback} which will be called to notify the * caller of the result of dns query. */ public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags, @NonNull @CallbackExecutor Executor executor, @Nullable CancellationSignal cancellationSignal, @NonNull InetAddressAnswerCallback callback) { if (cancellationSignal != null && cancellationSignal.isCanceled()) { return; } final Object lock = new Object(); final boolean queryIpv6 = haveIpv6(network); final boolean queryIpv4 = haveIpv4(network); final FileDescriptor v4fd; final FileDescriptor v6fd; int queryCount = 0; if (queryIpv6) { try { v6fd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); } catch (ErrnoException e) { executor.execute(() -> { callback.onQueryException(e); }); return; } queryCount++; } else v6fd = null; // TODO: Use device flag to controll the sleep time. // Avoiding gateways drop packets if queries are sent too close together try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException ex) { } if (queryIpv4) { try { v4fd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); } catch (ErrnoException e) { if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. executor.execute(() -> { callback.onQueryException(e); }); return; } queryCount++; } else v4fd = null; final InetAddressAnswerAccumulator accumulator = new InetAddressAnswerAccumulator(queryCount, callback); if (queryIpv6) registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock); if (queryIpv4) registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock); if (cancellationSignal == null) return; cancellationSignal.setOnCancelListener(() -> { synchronized (lock) { if (queryIpv4) cancelQuery(v4fd); if (queryIpv6) cancelQuery(v6fd); } }); } private <T> void registerFDListener(@NonNull Executor executor, Loading @@ -271,7 +417,7 @@ public final class DnsResolver { } byte[] answerbuf = null; try { answerbuf = resNetworkResult(fd); answerbuf = resNetworkResult(fd); // Closes fd, marks it invalid. } catch (ErrnoException e) { Log.e(TAG, "resNetworkResult:" + e.toString()); answerCallback.onQueryException(e); Loading @@ -291,19 +437,54 @@ public final class DnsResolver { }); } private void cancelQuery(@NonNull FileDescriptor queryfd) { if (!queryfd.valid()) return; Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd); resNetworkCancel(queryfd); // Closes fd, marks it invalid. } private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal, @NonNull FileDescriptor queryfd, @NonNull Object lock) { if (cancellationSignal == null) return; cancellationSignal.setOnCancelListener(() -> { synchronized (lock) { if (!queryfd.valid()) return; Looper.getMainLooper().getQueue() .removeOnFileDescriptorEventListener(queryfd); resNetworkCancel(queryfd); cancelQuery(queryfd); } }); } // 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; Loading