DnsPingerpublic final class DnsPinger extends android.os.Handler Performs a simple DNS "ping" by sending a "server status" query packet to the
DNS server. As long as the server replies, we consider it a success.
We do not use a simple hostname lookup because that could be cached and the
API may not differentiate between a time out and a failure lookup (which we
really care about).
|
Fields Summary |
---|
private static final boolean | DBG | private static final int | RECEIVE_POLL_INTERVAL_MS | private static final int | DNS_PORT | private static final int | SOCKET_TIMEOUT_MSShort socket timeout so we don't block one any 'receive' call | private static final Random | sRandomUsed to generate IDs | private static final AtomicInteger | sCounter | private ConnectivityManager | mConnectivityManager | private final android.content.Context | mContext | private final int | mConnectionType | private final android.os.Handler | mTarget | private final ArrayList | mDefaultDns | private String | TAG | private AtomicInteger | mCurrentToken | private static final int | BASE | public static final int | DNS_PING_RESULTAsync response packet for dns pings.
arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
arg2 is the delay, or is negative on error. | public static final int | TIMEOUTAn error code for a {@link #DNS_PING_RESULT} packet | public static final int | SOCKET_EXCEPTIONAn error code for a {@link #DNS_PING_RESULT} packet | private static final int | ACTION_PING_DNSSend a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping | private static final int | ACTION_LISTEN_FOR_RESPONSE | private static final int | ACTION_CANCEL_ALL_PINGS | private List | mActivePings | private int | mEventCounter | private static final byte[] | mDnsQuery |
Constructors Summary |
---|
public DnsPinger(android.content.Context context, String TAG, android.os.Looper looper, android.os.Handler target, int connectionType)
super(looper);
this.TAG = TAG;
mContext = context;
mTarget = target;
mConnectionType = connectionType;
if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
throw new IllegalArgumentException("Invalid connectionType in constructor: "
+ connectionType);
}
mDefaultDns = new ArrayList<InetAddress>();
mDefaultDns.add(getDefaultDns());
mEventCounter = 0;
|
Methods Summary |
---|
public void | cancelPings()
mCurrentToken.incrementAndGet();
obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
| private LinkProperties | getCurrentLinkProperties()
if (mConnectivityManager == null) {
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
}
return mConnectivityManager.getLinkProperties(mConnectionType);
| private java.net.InetAddress | getDefaultDns()
String dns = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DEFAULT_DNS_SERVER);
if (dns == null || dns.length() == 0) {
dns = mContext.getResources().getString(
com.android.internal.R.string.config_default_dns_server);
}
try {
return NetworkUtils.numericToInetAddress(dns);
} catch (IllegalArgumentException e) {
loge("getDefaultDns::malformed default dns address");
return null;
}
| public java.util.List | getDnsList()Returns a list of DNS addresses, coming from either the link properties of the
specified connection or the default system DNS if the link properties has no dnses.
LinkProperties curLinkProps = getCurrentLinkProperties();
if (curLinkProps == null) {
loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
return mDefaultDns;
}
Collection<InetAddress> dnses = curLinkProps.getDnsServers();
if (dnses == null || dnses.size() == 0) {
loge("getDns::LinkProps has null dns - returning default");
return mDefaultDns;
}
return new ArrayList<InetAddress>(dnses);
| public void | handleMessage(android.os.Message msg)
switch (msg.what) {
case ACTION_PING_DNS:
DnsArg dnsArg = (DnsArg) msg.obj;
if (dnsArg.seq != mCurrentToken.get()) {
break;
}
try {
ActivePing newActivePing = new ActivePing();
InetAddress dnsAddress = dnsArg.dns;
newActivePing.internalId = msg.arg1;
newActivePing.timeout = msg.arg2;
newActivePing.socket = new DatagramSocket();
// Set some socket properties
newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);
// Try to bind but continue ping if bind fails
try {
newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
getCurrentLinkProperties().getInterfaceName()));
} catch (Exception e) {
loge("sendDnsPing::Error binding to socket " + e);
}
newActivePing.packetId = (short) sRandom.nextInt();
byte[] buf = mDnsQuery.clone();
buf[0] = (byte) (newActivePing.packetId >> 8);
buf[1] = (byte) newActivePing.packetId;
// Send the DNS query
DatagramPacket packet = new DatagramPacket(buf,
buf.length, dnsAddress, DNS_PORT);
if (DBG) {
log("Sending a ping " + newActivePing.internalId +
" to " + dnsAddress.getHostAddress()
+ " with packetId " + newActivePing.packetId + ".");
}
newActivePing.socket.send(packet);
mActivePings.add(newActivePing);
mEventCounter++;
sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
RECEIVE_POLL_INTERVAL_MS);
} catch (IOException e) {
sendResponse(msg.arg1, -9999, SOCKET_EXCEPTION);
}
break;
case ACTION_LISTEN_FOR_RESPONSE:
if (msg.arg1 != mEventCounter) {
break;
}
for (ActivePing curPing : mActivePings) {
try {
/** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
byte[] responseBuf = new byte[2];
DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);
curPing.socket.receive(replyPacket);
// Check that ID field matches (we're throwing out the rest of the packet)
if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
responseBuf[1] == (byte) curPing.packetId) {
curPing.result =
(int) (SystemClock.elapsedRealtime() - curPing.start);
} else {
if (DBG) {
log("response ID didn't match, ignoring packet");
}
}
} catch (SocketTimeoutException e) {
// A timeout here doesn't mean anything - squelsh this exception
} catch (Exception e) {
if (DBG) {
log("DnsPinger.pingDns got socket exception: " + e);
}
curPing.result = SOCKET_EXCEPTION;
}
}
Iterator<ActivePing> iter = mActivePings.iterator();
while (iter.hasNext()) {
ActivePing curPing = iter.next();
if (curPing.result != null) {
sendResponse(curPing.internalId, curPing.packetId, curPing.result);
curPing.socket.close();
iter.remove();
} else if (SystemClock.elapsedRealtime() >
curPing.start + curPing.timeout) {
sendResponse(curPing.internalId, curPing.packetId, TIMEOUT);
curPing.socket.close();
iter.remove();
}
}
if (!mActivePings.isEmpty()) {
sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
RECEIVE_POLL_INTERVAL_MS);
}
break;
case ACTION_CANCEL_ALL_PINGS:
for (ActivePing activePing : mActivePings)
activePing.socket.close();
mActivePings.clear();
break;
}
| private void | log(java.lang.String s)
Log.d(TAG, s);
| private void | loge(java.lang.String s)
Log.e(TAG, s);
| public int | pingDnsAsync(java.net.InetAddress dns, int timeout, int delay)Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
specified at creation.
int id = sCounter.incrementAndGet();
sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
new DnsArg(dns, mCurrentToken.get())), delay);
return id;
| private void | sendResponse(int internalId, int externalId, int responseVal)
if(DBG) {
log("Responding to packet " + internalId +
" externalId " + externalId +
" and val " + responseVal);
}
mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
|
|