Merge "Refactor answer callback for async DNS query JAVA API" am: f1d0fd2642
am: 9a4c839725
Change-Id: I7538b247c5bd84d7f5b21be11b9bb1940fb9ba5a
This commit is contained in:
@@ -27225,10 +27225,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
|
||||
@@ -27238,12 +27237,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 {
|
||||
@@ -27557,6 +27567,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
public interface RawAnswerListener {
|
||||
/**
|
||||
* {@code byte[]} is {@code null} if query timed out
|
||||
*/
|
||||
void onAnswer(@Nullable byte[] answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* listener for receiving parsed answers
|
||||
*/
|
||||
public interface InetAddressAnswerListener {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void onAnswer(@NonNull List<InetAddress> addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance for DnsResolver
|
||||
*/
|
||||
public static DnsResolver getInstance() {
|
||||
public static @NonNull DnsResolver getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private DnsResolver() {}
|
||||
|
||||
/**
|
||||
* Pass in a blob and corresponding setting,
|
||||
* get a blob back asynchronously with the entire raw answer.
|
||||
* Answer parser for parsing raw answers
|
||||
*
|
||||
* @param <T> The type of the parsed answer
|
||||
*/
|
||||
public interface AnswerParser<T> {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@NonNull T parse(@NonNull byte[] rawAnswer) throws ParseException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for answer callbacks
|
||||
*
|
||||
* @param <T> The type of the parsed answer
|
||||
*/
|
||||
public abstract static class AnswerCallback<T> {
|
||||
/** @hide */
|
||||
public final AnswerParser<T> parser;
|
||||
|
||||
public AnswerCallback(@NonNull AnswerParser<T> parser) {
|
||||
this.parser = parser;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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()}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for receiving raw answers
|
||||
*/
|
||||
public abstract static class RawAnswerCallback extends AnswerCallback<byte[]> {
|
||||
public RawAnswerCallback() {
|
||||
super(rawAnswer -> rawAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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));
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user