PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/frameworks/base/core/java/android/net/DnsPinger.java

https://gitlab.com/brian0218/rk3066_r-box_android4.2.2_sdk
Java | 334 lines | 241 code | 35 blank | 58 comment | 34 complexity | d539aa5e563955f6a7c68b4badc1937e MD5 | raw file
  1. /*
  2. * Copyright (C) 2011 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.net;
  17. import android.content.Context;
  18. import android.os.Handler;
  19. import android.os.Looper;
  20. import android.os.Message;
  21. import android.os.SystemClock;
  22. import android.provider.Settings;
  23. import android.util.Log;
  24. import com.android.internal.util.Protocol;
  25. import java.io.IOException;
  26. import java.net.DatagramPacket;
  27. import java.net.DatagramSocket;
  28. import java.net.InetAddress;
  29. import java.net.NetworkInterface;
  30. import java.net.SocketTimeoutException;
  31. import java.util.ArrayList;
  32. import java.util.Collection;
  33. import java.util.Iterator;
  34. import java.util.List;
  35. import java.util.Random;
  36. import java.util.concurrent.atomic.AtomicInteger;
  37. /**
  38. * Performs a simple DNS "ping" by sending a "server status" query packet to the
  39. * DNS server. As long as the server replies, we consider it a success.
  40. * <p>
  41. * We do not use a simple hostname lookup because that could be cached and the
  42. * API may not differentiate between a time out and a failure lookup (which we
  43. * really care about).
  44. * <p>
  45. *
  46. * @hide
  47. */
  48. public final class DnsPinger extends Handler {
  49. private static final boolean DBG = false;
  50. private static final int RECEIVE_POLL_INTERVAL_MS = 200;
  51. private static final int DNS_PORT = 53;
  52. /** Short socket timeout so we don't block one any 'receive' call */
  53. private static final int SOCKET_TIMEOUT_MS = 1;
  54. /** Used to generate IDs */
  55. private static final Random sRandom = new Random();
  56. private static final AtomicInteger sCounter = new AtomicInteger();
  57. private ConnectivityManager mConnectivityManager = null;
  58. private final Context mContext;
  59. private final int mConnectionType;
  60. private final Handler mTarget;
  61. private final ArrayList<InetAddress> mDefaultDns;
  62. private String TAG;
  63. //Invalidates old dns requests upon a cancel
  64. private AtomicInteger mCurrentToken = new AtomicInteger();
  65. private static final int BASE = Protocol.BASE_DNS_PINGER;
  66. /**
  67. * Async response packet for dns pings.
  68. * arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
  69. * arg2 is the delay, or is negative on error.
  70. */
  71. public static final int DNS_PING_RESULT = BASE;
  72. /** An error code for a {@link #DNS_PING_RESULT} packet */
  73. public static final int TIMEOUT = -1;
  74. /** An error code for a {@link #DNS_PING_RESULT} packet */
  75. public static final int SOCKET_EXCEPTION = -2;
  76. /**
  77. * Send a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping
  78. */
  79. private static final int ACTION_PING_DNS = BASE + 1;
  80. private static final int ACTION_LISTEN_FOR_RESPONSE = BASE + 2;
  81. private static final int ACTION_CANCEL_ALL_PINGS = BASE + 3;
  82. private List<ActivePing> mActivePings = new ArrayList<ActivePing>();
  83. private int mEventCounter;
  84. private class ActivePing {
  85. DatagramSocket socket;
  86. int internalId;
  87. short packetId;
  88. int timeout;
  89. Integer result;
  90. long start = SystemClock.elapsedRealtime();
  91. }
  92. /* Message argument for ACTION_PING_DNS */
  93. private class DnsArg {
  94. InetAddress dns;
  95. int seq;
  96. DnsArg(InetAddress d, int s) {
  97. dns = d;
  98. seq = s;
  99. }
  100. }
  101. public DnsPinger(Context context, String TAG, Looper looper,
  102. Handler target, int connectionType) {
  103. super(looper);
  104. this.TAG = TAG;
  105. mContext = context;
  106. mTarget = target;
  107. mConnectionType = connectionType;
  108. if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
  109. throw new IllegalArgumentException("Invalid connectionType in constructor: "
  110. + connectionType);
  111. }
  112. mDefaultDns = new ArrayList<InetAddress>();
  113. mDefaultDns.add(getDefaultDns());
  114. mEventCounter = 0;
  115. }
  116. @Override
  117. public void handleMessage(Message msg) {
  118. switch (msg.what) {
  119. case ACTION_PING_DNS:
  120. DnsArg dnsArg = (DnsArg) msg.obj;
  121. if (dnsArg.seq != mCurrentToken.get()) {
  122. break;
  123. }
  124. try {
  125. ActivePing newActivePing = new ActivePing();
  126. InetAddress dnsAddress = dnsArg.dns;
  127. newActivePing.internalId = msg.arg1;
  128. newActivePing.timeout = msg.arg2;
  129. newActivePing.socket = new DatagramSocket();
  130. // Set some socket properties
  131. newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);
  132. // Try to bind but continue ping if bind fails
  133. try {
  134. newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
  135. getCurrentLinkProperties().getInterfaceName()));
  136. } catch (Exception e) {
  137. loge("sendDnsPing::Error binding to socket " + e);
  138. }
  139. newActivePing.packetId = (short) sRandom.nextInt();
  140. byte[] buf = mDnsQuery.clone();
  141. buf[0] = (byte) (newActivePing.packetId >> 8);
  142. buf[1] = (byte) newActivePing.packetId;
  143. // Send the DNS query
  144. DatagramPacket packet = new DatagramPacket(buf,
  145. buf.length, dnsAddress, DNS_PORT);
  146. if (DBG) {
  147. log("Sending a ping " + newActivePing.internalId +
  148. " to " + dnsAddress.getHostAddress()
  149. + " with packetId " + newActivePing.packetId + ".");
  150. }
  151. newActivePing.socket.send(packet);
  152. mActivePings.add(newActivePing);
  153. mEventCounter++;
  154. sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
  155. RECEIVE_POLL_INTERVAL_MS);
  156. } catch (IOException e) {
  157. sendResponse(msg.arg1, -9999, SOCKET_EXCEPTION);
  158. }
  159. break;
  160. case ACTION_LISTEN_FOR_RESPONSE:
  161. if (msg.arg1 != mEventCounter) {
  162. break;
  163. }
  164. for (ActivePing curPing : mActivePings) {
  165. try {
  166. /** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
  167. byte[] responseBuf = new byte[2];
  168. DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);
  169. curPing.socket.receive(replyPacket);
  170. // Check that ID field matches (we're throwing out the rest of the packet)
  171. if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
  172. responseBuf[1] == (byte) curPing.packetId) {
  173. curPing.result =
  174. (int) (SystemClock.elapsedRealtime() - curPing.start);
  175. } else {
  176. if (DBG) {
  177. log("response ID didn't match, ignoring packet");
  178. }
  179. }
  180. } catch (SocketTimeoutException e) {
  181. // A timeout here doesn't mean anything - squelsh this exception
  182. } catch (Exception e) {
  183. if (DBG) {
  184. log("DnsPinger.pingDns got socket exception: " + e);
  185. }
  186. curPing.result = SOCKET_EXCEPTION;
  187. }
  188. }
  189. Iterator<ActivePing> iter = mActivePings.iterator();
  190. while (iter.hasNext()) {
  191. ActivePing curPing = iter.next();
  192. if (curPing.result != null) {
  193. sendResponse(curPing.internalId, curPing.packetId, curPing.result);
  194. curPing.socket.close();
  195. iter.remove();
  196. } else if (SystemClock.elapsedRealtime() >
  197. curPing.start + curPing.timeout) {
  198. sendResponse(curPing.internalId, curPing.packetId, TIMEOUT);
  199. curPing.socket.close();
  200. iter.remove();
  201. }
  202. }
  203. if (!mActivePings.isEmpty()) {
  204. sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
  205. RECEIVE_POLL_INTERVAL_MS);
  206. }
  207. break;
  208. case ACTION_CANCEL_ALL_PINGS:
  209. for (ActivePing activePing : mActivePings)
  210. activePing.socket.close();
  211. mActivePings.clear();
  212. break;
  213. }
  214. }
  215. /**
  216. * Returns a list of DNS addresses, coming from either the link properties of the
  217. * specified connection or the default system DNS if the link properties has no dnses.
  218. * @return a non-empty non-null list
  219. */
  220. public List<InetAddress> getDnsList() {
  221. LinkProperties curLinkProps = getCurrentLinkProperties();
  222. if (curLinkProps == null) {
  223. loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
  224. return mDefaultDns;
  225. }
  226. Collection<InetAddress> dnses = curLinkProps.getDnses();
  227. if (dnses == null || dnses.size() == 0) {
  228. loge("getDns::LinkProps has null dns - returning default");
  229. return mDefaultDns;
  230. }
  231. return new ArrayList<InetAddress>(dnses);
  232. }
  233. /**
  234. * Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
  235. * specified at creation.
  236. * @param dns address of dns server to ping
  237. * @param timeout timeout for ping
  238. * @return an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.
  239. */
  240. public int pingDnsAsync(InetAddress dns, int timeout, int delay) {
  241. int id = sCounter.incrementAndGet();
  242. sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
  243. new DnsArg(dns, mCurrentToken.get())), delay);
  244. return id;
  245. }
  246. public void cancelPings() {
  247. mCurrentToken.incrementAndGet();
  248. obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
  249. }
  250. private void sendResponse(int internalId, int externalId, int responseVal) {
  251. if(DBG) {
  252. log("Responding to packet " + internalId +
  253. " externalId " + externalId +
  254. " and val " + responseVal);
  255. }
  256. mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
  257. }
  258. private LinkProperties getCurrentLinkProperties() {
  259. if (mConnectivityManager == null) {
  260. mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
  261. Context.CONNECTIVITY_SERVICE);
  262. }
  263. return mConnectivityManager.getLinkProperties(mConnectionType);
  264. }
  265. private InetAddress getDefaultDns() {
  266. String dns = Settings.Global.getString(mContext.getContentResolver(),
  267. Settings.Global.DEFAULT_DNS_SERVER);
  268. if (dns == null || dns.length() == 0) {
  269. dns = mContext.getResources().getString(
  270. com.android.internal.R.string.config_default_dns_server);
  271. }
  272. try {
  273. return NetworkUtils.numericToInetAddress(dns);
  274. } catch (IllegalArgumentException e) {
  275. loge("getDefaultDns::malformed default dns address");
  276. return null;
  277. }
  278. }
  279. private static final byte[] mDnsQuery = new byte[] {
  280. 0, 0, // [0-1] is for ID (will set each time)
  281. 1, 0, // [2-3] are flags. Set byte[2] = 1 for recursion desired (RD) on. Currently on.
  282. 0, 1, // [4-5] bytes are for number of queries (QCOUNT)
  283. 0, 0, // [6-7] unused count field for dns response packets
  284. 0, 0, // [8-9] unused count field for dns response packets
  285. 0, 0, // [10-11] unused count field for dns response packets
  286. 3, 'w', 'w', 'w',
  287. 6, 'g', 'o', 'o', 'g', 'l', 'e',
  288. 3, 'c', 'o', 'm',
  289. 0, // null terminator of address (also called empty TLD)
  290. 0, 1, // QTYPE, set to 1 = A (host address)
  291. 0, 1 // QCLASS, set to 1 = IN (internet)
  292. };
  293. private void log(String s) {
  294. Log.d(TAG, s);
  295. }
  296. private void loge(String s) {
  297. Log.e(TAG, s);
  298. }
  299. }