diff options
| -rwxr-xr-x | api/current.txt | 28 | ||||
| -rw-r--r-- | core/java/android/net/DnsPacket.java | 17 | ||||
| -rw-r--r-- | core/java/android/net/DnsResolver.java | 233 | ||||
| -rw-r--r-- | core/java/android/net/ParseException.java | 11 | ||||
| -rw-r--r-- | tests/net/java/android/net/DnsPacketTest.java | 2 |
5 files changed, 159 insertions, 132 deletions
diff --git a/api/current.txt b/api/current.txt index 0fa6cc623bef..4a8ad912186e 100755 --- a/api/current.txt +++ b/api/current.txt @@ -27227,10 +27227,9 @@ package android.net { } public final class DnsResolver { - method public static android.net.DnsResolver getInstance(); - method public void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException; - method public void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException; - method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.InetAddressAnswerListener) throws android.system.ErrnoException; + method @NonNull public static android.net.DnsResolver getInstance(); + method public <T> void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.AnswerCallback<T>); + method public <T> void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.AnswerCallback<T>); 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 @@ -27240,12 +27239,23 @@ package android.net { field public static final int TYPE_AAAA = 28; // 0x1c } - public static interface DnsResolver.InetAddressAnswerListener { - method public void onAnswer(@NonNull java.util.List<java.net.InetAddress>); + public abstract static class DnsResolver.AnswerCallback<T> { + ctor public DnsResolver.AnswerCallback(@NonNull android.net.DnsResolver.AnswerParser<T>); + method public abstract void onAnswer(@NonNull T); + method public abstract void onParseException(@NonNull android.net.ParseException); + method public abstract void onQueryException(@NonNull android.system.ErrnoException); } - public static interface DnsResolver.RawAnswerListener { - method public void onAnswer(@Nullable byte[]); + public static interface DnsResolver.AnswerParser<T> { + method @NonNull public T parse(@NonNull byte[]) throws android.net.ParseException; + } + + public abstract static class DnsResolver.InetAddressAnswerCallback extends android.net.DnsResolver.AnswerCallback<java.util.List<java.net.InetAddress>> { + ctor public DnsResolver.InetAddressAnswerCallback(); + } + + public abstract static class DnsResolver.RawAnswerCallback extends android.net.DnsResolver.AnswerCallback<byte[]> { + ctor public DnsResolver.RawAnswerCallback(); } public class InetAddresses { @@ -27559,6 +27569,8 @@ package android.net { } public class ParseException extends java.lang.RuntimeException { + ctor public ParseException(@NonNull String); + ctor public ParseException(@NonNull String, @NonNull Throwable); field public String response; } diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java index 0ac02b1b7b37..83e57e0a047b 100644 --- a/core/java/android/net/DnsPacket.java +++ b/core/java/android/net/DnsPacket.java @@ -71,7 +71,7 @@ public abstract class DnsPacket { } /** - * It's used both for DNS questions and DNS resource records. + * Superclass for DNS questions and DNS resource records. * * DNS questions (No TTL/RDATA) * DNS resource records (With TTL/RDATA) @@ -96,12 +96,13 @@ public abstract class DnsPacket { /** * Create a new DnsRecord from a positioned ByteBuffer. * - * @param ByteBuffer input of record, must be in network byte order - * (which is the default). * Reads the passed ByteBuffer from its current position and decodes a DNS record. * When this constructor returns, the reading position of the ByteBuffer has been * advanced to the end of the DNS header record. * This is meant to chain with other methods reading a DNS response in sequence. + * + * @param ByteBuffer input of record, must be in network byte order + * (which is the default). */ DnsRecord(int recordType, @NonNull ByteBuffer buf) throws BufferUnderflowException, ParseException { @@ -205,16 +206,6 @@ public abstract class DnsPacket { protected final DnsHeader mHeader; protected final List<DnsRecord>[] mRecords; - public static class ParseException extends Exception { - public ParseException(String msg) { - super(msg); - } - - public ParseException(String msg, Throwable cause) { - super(msg, cause); - } - } - protected DnsPacket(@NonNull byte[] data) throws ParseException { if (null == data) throw new ParseException("Parse header failed, null input data"); final ByteBuffer buffer; diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index d3bc3e66fbee..f24895805591 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -37,8 +37,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; - /** * Dns resolver class for asynchronous dns querying @@ -81,66 +79,138 @@ public final class DnsResolver { public static final int FLAG_NO_CACHE_STORE = 1 << 1; public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2; - private static final int DNS_RAW_RESPONSE = 1; - private static final int NETID_UNSET = 0; private static final DnsResolver sInstance = new DnsResolver(); /** - * listener for receiving raw answers + * Get instance for DnsResolver + */ + public static @NonNull DnsResolver getInstance() { + return sInstance; + } + + private DnsResolver() {} + + /** + * Answer parser for parsing raw answers + * + * @param <T> The type of the parsed answer */ - public interface RawAnswerListener { + public interface AnswerParser<T> { /** - * {@code byte[]} is {@code null} if query timed out + * Creates a <T> answer by parsing the given raw answer. + * + * @param rawAnswer the raw answer to be parsed + * @return a parsed <T> answer + * @throws ParseException if parsing failed */ - void onAnswer(@Nullable byte[] answer); + @NonNull T parse(@NonNull byte[] rawAnswer) throws ParseException; } /** - * listener for receiving parsed answers + * Base class for answer callbacks + * + * @param <T> The type of the parsed answer */ - public interface InetAddressAnswerListener { + public abstract static class AnswerCallback<T> { + /** @hide */ + public final AnswerParser<T> parser; + + public AnswerCallback(@NonNull AnswerParser<T> parser) { + this.parser = parser; + }; + /** - * Will be called exactly once with all the answers to the query. - * size of addresses will be zero if no available answer could be parsed. + * Success response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked when the answer to a query was successfully parsed. + * + * @param answer parsed answer to the query. + * + * {@see android.net.DnsResolver#query query()} */ - void onAnswer(@NonNull List<InetAddress> addresses); + public abstract void onAnswer(@NonNull T answer); + + /** + * Error response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked when there is no valid answer to + * {@link android.net.DnsResolver#query query()} + * + * @param exception a {@link ParseException} object with additional + * detail regarding the failure + */ + public abstract void onParseException(@NonNull ParseException exception); + + /** + * Error response to + * {@link android.net.DnsResolver#query query()}. + * + * Invoked if an error happens when + * issuing the DNS query or receiving the result. + * {@link android.net.DnsResolver#query query()} + * + * @param exception an {@link ErrnoException} object with additional detail + * regarding the failure + */ + public abstract void onQueryException(@NonNull ErrnoException exception); } /** - * Get instance for DnsResolver + * Callback for receiving raw answers */ - public static DnsResolver getInstance() { - return sInstance; + public abstract static class RawAnswerCallback extends AnswerCallback<byte[]> { + public RawAnswerCallback() { + super(rawAnswer -> rawAnswer); + } } - private DnsResolver() {} + /** + * Callback for receiving parsed {@link InetAddress} answers + * + * Note that if the answer does not contain any IP addresses, + * onAnswer will be called with an empty list. + */ + public abstract static class InetAddressAnswerCallback + extends AnswerCallback<List<InetAddress>> { + public InetAddressAnswerCallback() { + super(rawAnswer -> new DnsAddressAnswer(rawAnswer).getAddresses()); + } + } /** - * Pass in a blob and corresponding setting, - * get a blob back asynchronously with the entire raw answer. + * Pass in a blob and corresponding flags, get an answer back asynchronously + * through {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * {@code null} for query on default network. * @param query blob message * @param flags flags as a combination of the FLAGS_* constants * @param handler {@link Handler} to specify the thread - * upon which the {@link RawAnswerListener} will be invoked. - * @param listener a {@link RawAnswerListener} which will be called to notify the caller + * upon which the {@link AnswerCallback} will be invoked. + * @param callback an {@link AnswerCallback} which will be called to notify the caller * of the result of dns query. */ - public void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags, - @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException { - final FileDescriptor queryfd = resNetworkSend((network != null + public <T> void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags, + @NonNull Handler handler, @NonNull AnswerCallback<T> callback) { + final FileDescriptor queryfd; + try { + queryfd = resNetworkSend((network != null ? network.netId : NETID_UNSET), query, query.length, flags); - registerFDListener(handler.getLooper().getQueue(), queryfd, - answerbuf -> listener.onAnswer(answerbuf)); + } catch (ErrnoException e) { + callback.onQueryException(e); + return; + } + + registerFDListener(handler.getLooper().getQueue(), queryfd, callback); } /** - * Pass in a domain name and corresponding setting, - * get a blob back asynchronously with the entire raw answer. + * Pass in a domain name and corresponding setting, get an answer back asynchronously + * through {@link AnswerCallback}. * * @param network {@link Network} specifying which network for querying. * {@code null} for query on default network. @@ -149,52 +219,26 @@ public final class DnsResolver { * @param nsType dns resource record (RR) type as one of the TYPE_* constants * @param flags flags as a combination of the FLAGS_* constants * @param handler {@link Handler} to specify the thread - * upon which the {@link RawAnswerListener} will be invoked. - * @param listener a {@link RawAnswerListener} which will be called to notify the caller + * upon which the {@link AnswerCallback} will be invoked. + * @param callback an {@link AnswerCallback} which will be called to notify the caller * of the result of dns query. */ - public void query(@Nullable Network network, @NonNull String domain, @QueryClass int nsClass, - @QueryType int nsType, @QueryFlag int flags, - @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException { - final FileDescriptor queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); - registerFDListener(handler.getLooper().getQueue(), queryfd, - answerbuf -> listener.onAnswer(answerbuf)); - } - - /** - * Pass in a domain name and corresponding setting, - * get back a set of InetAddresses asynchronously. - * - * @param network {@link Network} specifying which network for querying. - * {@code null} for query on default network. - * @param domain domain name for querying - * @param flags flags as a combination of the FLAGS_* constants - * @param handler {@link Handler} to specify the thread - * upon which the {@link InetAddressAnswerListener} will be invoked. - * @param listener an {@link InetAddressAnswerListener} 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 Handler handler, @NonNull InetAddressAnswerListener listener) - throws ErrnoException { - final FileDescriptor v4fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); - final FileDescriptor v6fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); - - final InetAddressAnswerAccumulator accmulator = - new InetAddressAnswerAccumulator(2, listener); - final Consumer<byte[]> consumer = answerbuf -> - accmulator.accumulate(parseAnswers(answerbuf)); - - registerFDListener(handler.getLooper().getQueue(), v4fd, consumer); - registerFDListener(handler.getLooper().getQueue(), v6fd, consumer); + public <T> void query(@Nullable Network network, @NonNull String domain, + @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags, + @NonNull Handler handler, @NonNull AnswerCallback<T> callback) { + final FileDescriptor queryfd; + try { + queryfd = resNetworkQuery((network != null + ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); + } catch (ErrnoException e) { + callback.onQueryException(e); + return; + } + registerFDListener(handler.getLooper().getQueue(), queryfd, callback); } - private void registerFDListener(@NonNull MessageQueue queue, - @NonNull FileDescriptor queryfd, @NonNull Consumer<byte[]> answerConsumer) { + private <T> void registerFDListener(@NonNull MessageQueue queue, + @NonNull FileDescriptor queryfd, @NonNull AnswerCallback<T> answerCallback) { queue.addOnFileDescriptorEventListener( queryfd, FD_EVENTS, @@ -207,15 +251,22 @@ public final class DnsResolver { answerbuf = resNetworkResult(fd); } catch (ErrnoException e) { Log.e(TAG, "resNetworkResult:" + e.toString()); + answerCallback.onQueryException(e); + return 0; + } + + try { + answerCallback.onAnswer(answerCallback.parser.parse(answerbuf)); + } catch (ParseException e) { + answerCallback.onParseException(e); } - answerConsumer.accept(answerbuf); // Unregister this fd listener return 0; }); } - private class DnsAddressAnswer extends DnsPacket { + private static class DnsAddressAnswer extends DnsPacket { private static final String TAG = "DnsResolver.DnsAddressAnswer"; private static final boolean DBG = false; @@ -226,12 +277,6 @@ public final class DnsResolver { if ((mHeader.flags & (1 << 15)) == 0) { throw new ParseException("Not an answer packet"); } - if (mHeader.rcode != 0) { - throw new ParseException("Response error, rcode:" + mHeader.rcode); - } - if (mHeader.getRecordCount(ANSECTION) == 0) { - throw new ParseException("No available answer"); - } if (mHeader.getRecordCount(QDSECTION) == 0) { throw new ParseException("No question found"); } @@ -241,6 +286,8 @@ public final class DnsResolver { public @NonNull List<InetAddress> getAddresses() { final List<InetAddress> results = new ArrayList<InetAddress>(); + if (mHeader.getRecordCount(ANSECTION) == 0) return results; + for (final DnsRecord ansSec : mRecords[ANSECTION]) { // Only support A and AAAA, also ignore answers if query type != answer type. int nsType = ansSec.nsType; @@ -259,34 +306,4 @@ public final class DnsResolver { } } - private @Nullable List<InetAddress> parseAnswers(@Nullable byte[] data) { - try { - return (data == null) ? null : new DnsAddressAnswer(data).getAddresses(); - } catch (DnsPacket.ParseException e) { - Log.e(TAG, "Parse answer fail " + e.getMessage()); - return null; - } - } - - private class InetAddressAnswerAccumulator { - private final List<InetAddress> mAllAnswers; - private final InetAddressAnswerListener mAnswerListener; - private final int mTargetAnswerCount; - private int mReceivedAnswerCount = 0; - - InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerListener listener) { - mTargetAnswerCount = size; - mAllAnswers = new ArrayList<>(); - mAnswerListener = listener; - } - - public void accumulate(@Nullable List<InetAddress> answer) { - if (null != answer) { - mAllAnswers.addAll(answer); - } - if (++mReceivedAnswerCount == mTargetAnswerCount) { - mAnswerListener.onAnswer(mAllAnswers); - } - } - } } diff --git a/core/java/android/net/ParseException.java b/core/java/android/net/ParseException.java index 2380e863d043..9d4727a84bc0 100644 --- a/core/java/android/net/ParseException.java +++ b/core/java/android/net/ParseException.java @@ -16,15 +16,22 @@ package android.net; +import android.annotation.NonNull; + /** - * Thrown when parsing a URL fails. + * Thrown when parsing failed. */ // See non-public class {@link WebAddress}. public class ParseException extends RuntimeException { public String response; - ParseException(String response) { + public ParseException(@NonNull String response) { super(response); this.response = response; } + + public ParseException(@NonNull String response, @NonNull Throwable cause) { + super(response, cause); + this.response = response; + } } diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java index 9ede2b85af00..975abf416944 100644 --- a/tests/net/java/android/net/DnsPacketTest.java +++ b/tests/net/java/android/net/DnsPacketTest.java @@ -69,7 +69,7 @@ public class DnsPacketTest { try { new TestDnsPacket(null); fail("Exception not thrown for null byte array"); - } catch (DnsPacket.ParseException e) { + } catch (ParseException e) { } } |