FileDocCategorySizeDatePackage
DnsPinger.javaAPI DocAndroid 5.1 API13189Thu Mar 12 22:22:10 GMT 2015android.net

DnsPinger

public 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).

hide

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_MS
Short socket timeout so we don't block one any 'receive' call
private static final Random
sRandom
Used 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_RESULT
Async 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
TIMEOUT
An error code for a {@link #DNS_PING_RESULT} packet
public static final int
SOCKET_EXCEPTION
An error code for a {@link #DNS_PING_RESULT} packet
private static final int
ACTION_PING_DNS
Send 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 voidcancelPings()

        mCurrentToken.incrementAndGet();
        obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
    
private LinkPropertiesgetCurrentLinkProperties()

        if (mConnectivityManager == null) {
            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
                    Context.CONNECTIVITY_SERVICE);
        }

        return mConnectivityManager.getLinkProperties(mConnectionType);
    
private java.net.InetAddressgetDefaultDns()

        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.ListgetDnsList()
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.

return
a non-empty non-null list

        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 voidhandleMessage(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 voidlog(java.lang.String s)


        
        Log.d(TAG, s);
    
private voidloge(java.lang.String s)

        Log.e(TAG, s);
    
public intpingDnsAsync(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.

param
dns address of dns server to ping
param
timeout timeout for ping
return
an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.

        int id = sCounter.incrementAndGet();
        sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
                new DnsArg(dns, mCurrentToken.get())), delay);
        return id;
    
private voidsendResponse(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));