blob: bf1b6e65c8f6845894e72df56467ab1e796e2ffd [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
17package com.android.server;
18
19import android.content.BroadcastReceiver;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.database.ContentObserver;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -080025import android.net.ConnectivityManager;
26import android.net.LinkProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.net.NetworkInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.net.wifi.ScanResult;
29import android.net.wifi.WifiInfo;
30import android.net.wifi.WifiManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Handler;
32import android.os.Looper;
33import android.os.Message;
34import android.provider.Settings;
35import android.text.TextUtils;
Joe Onorato8a9b2202010-02-26 18:56:32 -080036import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
38import java.io.IOException;
39import java.net.DatagramPacket;
40import java.net.DatagramSocket;
41import java.net.InetAddress;
42import java.net.SocketException;
43import java.net.SocketTimeoutException;
44import java.net.UnknownHostException;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -080045import java.util.Collection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.util.List;
47import java.util.Random;
48
49/**
50 * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
51 * network with multiple access points. After the framework successfully
52 * connects to an access point, the watchdog verifies whether the DNS server is
53 * reachable. If not, the watchdog blacklists the current access point, leading
54 * to a connection on another access point within the same network.
55 * <p>
56 * The watchdog has a few safeguards:
57 * <ul>
58 * <li>Only monitor networks with multiple access points
59 * <li>Only check at most {@link #getMaxApChecks()} different access points
60 * within the network before giving up
61 * <p>
62 * The watchdog checks for connectivity on an access point by ICMP pinging the
63 * DNS. There are settings that allow disabling the watchdog, or tweaking the
64 * acceptable packet loss (and other various parameters).
65 * <p>
66 * The core logic of the watchdog is done on the main watchdog thread. Wi-Fi
67 * callbacks can come in on other threads, so we must queue messages to the main
68 * watchdog thread's handler. Most (if not all) state is only written to from
69 * the main thread.
70 *
71 * {@hide}
72 */
73public class WifiWatchdogService {
74 private static final String TAG = "WifiWatchdogService";
Joe Onorato43a17652011-04-06 19:22:23 -070075 private static final boolean V = false || false;
76 private static final boolean D = true || false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private Context mContext;
79 private ContentResolver mContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private WifiManager mWifiManager;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -080081 private ConnectivityManager mConnectivityManager;
82
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
84 * The main watchdog thread.
85 */
86 private WifiWatchdogThread mThread;
87 /**
88 * The handler for the main watchdog thread.
89 */
90 private WifiWatchdogHandler mHandler;
91
Irfan Sheriff7b009782010-03-11 16:37:45 -080092 private ContentObserver mContentObserver;
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 /**
95 * The current watchdog state. Only written from the main thread!
96 */
97 private WatchdogState mState = WatchdogState.IDLE;
98 /**
99 * The SSID of the network that the watchdog is currently monitoring. Only
100 * touched in the main thread!
101 */
102 private String mSsid;
103 /**
104 * The number of access points in the current network ({@link #mSsid}) that
105 * have been checked. Only touched in the main thread!
106 */
107 private int mNumApsChecked;
108 /** Whether the current AP check should be canceled. */
109 private boolean mShouldCancel;
110
Irfan Sheriff0d255342010-07-28 09:35:20 -0700111 WifiWatchdogService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 mContext = context;
113 mContentResolver = context.getContentResolver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
115
116 createThread();
117
118 // The content observer to listen needs a handler, which createThread creates
119 registerForSettingsChanges();
120 if (isWatchdogEnabled()) {
121 registerForWifiBroadcasts();
122 }
123
124 if (V) {
125 myLogV("WifiWatchdogService: Created");
126 }
127 }
128
129 /**
130 * Observes the watchdog on/off setting, and takes action when changed.
131 */
132 private void registerForSettingsChanges() {
133 ContentResolver contentResolver = mContext.getContentResolver();
134 contentResolver.registerContentObserver(
135 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false,
Irfan Sheriff7b009782010-03-11 16:37:45 -0800136 mContentObserver = new ContentObserver(mHandler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 @Override
138 public void onChange(boolean selfChange) {
139 if (isWatchdogEnabled()) {
140 registerForWifiBroadcasts();
141 } else {
142 unregisterForWifiBroadcasts();
143 if (mHandler != null) {
144 mHandler.disableWatchdog();
145 }
146 }
147 }
148 });
149 }
150
151 /**
152 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
153 */
154 private boolean isWatchdogEnabled() {
155 return Settings.Secure.getInt(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
156 }
157
158 /**
159 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT
160 */
161 private int getApCount() {
162 return Settings.Secure.getInt(mContentResolver,
163 Settings.Secure.WIFI_WATCHDOG_AP_COUNT, 2);
164 }
165
166 /**
167 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT
168 */
169 private int getInitialIgnoredPingCount() {
170 return Settings.Secure.getInt(mContentResolver,
171 Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT , 2);
172 }
173
174 /**
175 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT
176 */
177 private int getPingCount() {
178 return Settings.Secure.getInt(mContentResolver,
179 Settings.Secure.WIFI_WATCHDOG_PING_COUNT, 4);
180 }
181
182 /**
183 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS
184 */
185 private int getPingTimeoutMs() {
186 return Settings.Secure.getInt(mContentResolver,
187 Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS, 500);
188 }
189
190 /**
191 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS
192 */
193 private int getPingDelayMs() {
194 return Settings.Secure.getInt(mContentResolver,
195 Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250);
196 }
197
198 /**
199 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE
200 */
201 private int getAcceptablePacketLossPercentage() {
202 return Settings.Secure.getInt(mContentResolver,
203 Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, 25);
204 }
205
206 /**
207 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS
208 */
209 private int getMaxApChecks() {
210 return Settings.Secure.getInt(mContentResolver,
211 Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS, 7);
212 }
213
214 /**
215 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED
216 */
217 private boolean isBackgroundCheckEnabled() {
218 return Settings.Secure.getInt(mContentResolver,
219 Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, 1) == 1;
220 }
221
222 /**
223 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS
224 */
225 private int getBackgroundCheckDelayMs() {
226 return Settings.Secure.getInt(mContentResolver,
227 Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, 60000);
228 }
229
230 /**
231 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS
232 */
233 private int getBackgroundCheckTimeoutMs() {
234 return Settings.Secure.getInt(mContentResolver,
235 Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, 1000);
236 }
237
238 /**
239 * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WATCH_LIST
240 * @return the comma-separated list of SSIDs
241 */
242 private String getWatchList() {
243 return Settings.Secure.getString(mContentResolver,
244 Settings.Secure.WIFI_WATCHDOG_WATCH_LIST);
245 }
246
247 /**
248 * Registers to receive the necessary Wi-Fi broadcasts.
249 */
250 private void registerForWifiBroadcasts() {
251 IntentFilter intentFilter = new IntentFilter();
252 intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
254 mContext.registerReceiver(mReceiver, intentFilter);
255 }
256
257 /**
258 * Unregisters from receiving the Wi-Fi broadcasts.
259 */
260 private void unregisterForWifiBroadcasts() {
261 mContext.unregisterReceiver(mReceiver);
262 }
263
264 /**
265 * Creates the main watchdog thread, including waiting for the handler to be
266 * created.
267 */
268 private void createThread() {
269 mThread = new WifiWatchdogThread();
270 mThread.start();
271 waitForHandlerCreation();
272 }
273
274 /**
Irfan Sheriff7b009782010-03-11 16:37:45 -0800275 * Unregister broadcasts and quit the watchdog thread
276 */
Irfan Sheriffa2a1b912010-06-07 09:03:04 -0700277 //TODO: Change back to running WWS when needed
278// private void quit() {
279// unregisterForWifiBroadcasts();
280// mContext.getContentResolver().unregisterContentObserver(mContentObserver);
281// mHandler.removeAllActions();
282// mHandler.getLooper().quit();
283// }
Irfan Sheriff7b009782010-03-11 16:37:45 -0800284
285 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 * Waits for the main watchdog thread to create the handler.
287 */
288 private void waitForHandlerCreation() {
289 synchronized(this) {
290 while (mHandler == null) {
291 try {
292 // Wait for the handler to be set by the other thread
293 wait();
294 } catch (InterruptedException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800295 Slog.e(TAG, "Interrupted while waiting on handler.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
297 }
298 }
299 }
300
301 // Utility methods
302
303 /**
304 * Logs with the current thread.
305 */
306 private static void myLogV(String message) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800307 Slog.v(TAG, "(" + Thread.currentThread().getName() + ") " + message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 }
309
310 private static void myLogD(String message) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800311 Slog.d(TAG, "(" + Thread.currentThread().getName() + ") " + message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313
314 /**
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800315 * Gets the first DNS of the current AP.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 *
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800317 * @return The first DNS of the current AP.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 */
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800319 private InetAddress getDns() {
320 if (mConnectivityManager == null) {
321 mConnectivityManager = (ConnectivityManager)mContext.getSystemService(
322 Context.CONNECTIVITY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800324
325 LinkProperties linkProperties = mConnectivityManager.getLinkProperties(
326 ConnectivityManager.TYPE_WIFI);
327 if (linkProperties == null) return null;
328
329 Collection<InetAddress> dnses = linkProperties.getDnses();
330 if (dnses == null || dnses.size() == 0) return null;
331
332 return dnses.iterator().next();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 /**
336 * Checks whether the DNS can be reached using multiple attempts according
337 * to the current setting values.
338 *
339 * @return Whether the DNS is reachable
340 */
341 private boolean checkDnsConnectivity() {
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800342 InetAddress dns = getDns();
343 if (dns == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 if (V) {
345 myLogV("checkDnsConnectivity: Invalid DNS, returning false");
346 }
347 return false;
348 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 if (V) {
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800351 myLogV("checkDnsConnectivity: Checking " + dns.getHostAddress() + " for connectivity");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353
354 int numInitialIgnoredPings = getInitialIgnoredPingCount();
355 int numPings = getPingCount();
356 int pingDelay = getPingDelayMs();
357 int acceptableLoss = getAcceptablePacketLossPercentage();
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */
360 int ignoredPingCounter = 0;
361 int pingCounter = 0;
362 int successCounter = 0;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 // No connectivity check needed
365 if (numPings == 0) {
366 return true;
367 }
368
369 // Do the initial pings that we ignore
370 for (; ignoredPingCounter < numInitialIgnoredPings; ignoredPingCounter++) {
371 if (shouldCancel()) return false;
372
373 boolean dnsAlive = DnsPinger.isDnsReachable(dns, getPingTimeoutMs());
374 if (dnsAlive) {
375 /*
376 * Successful "ignored" pings are *not* ignored (they count in the total number
377 * of pings), but failures are really ignored.
378 */
379 pingCounter++;
380 successCounter++;
381 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800384 Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386
387 if (shouldCancel()) return false;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 try {
390 Thread.sleep(pingDelay);
391 } catch (InterruptedException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800392 Slog.w(TAG, "Interrupted while pausing between pings", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 }
394 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 // Do the pings that we use to measure packet loss
397 for (; pingCounter < numPings; pingCounter++) {
398 if (shouldCancel()) return false;
399
400 if (DnsPinger.isDnsReachable(dns, getPingTimeoutMs())) {
401 successCounter++;
402 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800403 Slog.v(TAG, " +");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 }
405 } else {
406 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800407 Slog.v(TAG, " -");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 }
409 }
410
411 if (shouldCancel()) return false;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 try {
414 Thread.sleep(pingDelay);
415 } catch (InterruptedException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800416 Slog.w(TAG, "Interrupted while pausing between pings", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 }
418 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 int packetLossPercentage = 100 * (numPings - successCounter) / numPings;
421 if (D) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800422 Slog.d(TAG, packetLossPercentage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 + "% packet loss (acceptable is " + acceptableLoss + "%)");
424 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800425
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 return !shouldCancel() && (packetLossPercentage <= acceptableLoss);
427 }
428
429 private boolean backgroundCheckDnsConnectivity() {
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800430 InetAddress dns = getDns();
431
432 if (dns == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 if (V) {
434 myLogV("backgroundCheckDnsConnectivity: DNS is empty, returning false");
435 }
436 return false;
437 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800438
439 if (false && V) {
440 myLogV("backgroundCheckDnsConnectivity: Background checking " +
441 dns.getHostAddress() + " for connectivity");
442 }
443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 return DnsPinger.isDnsReachable(dns, getBackgroundCheckTimeoutMs());
445 }
Robert Greenwalt8e9abc52011-02-16 10:37:50 -0800446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 /**
448 * Signals the current action to cancel.
449 */
450 private void cancelCurrentAction() {
451 mShouldCancel = true;
452 }
453
454 /**
455 * Helper to check whether to cancel.
456 *
457 * @return Whether to cancel processing the action.
458 */
459 private boolean shouldCancel() {
460 if (V && mShouldCancel) {
461 myLogV("shouldCancel: Cancelling");
462 }
463
464 return mShouldCancel;
465 }
466
467 // Wi-Fi initiated callbacks (could be executed in another thread)
468
469 /**
470 * Called when connected to an AP (this can be the next AP in line, or
471 * it can be a completely different network).
472 *
473 * @param ssid The SSID of the access point.
474 * @param bssid The BSSID of the access point.
475 */
476 private void onConnected(String ssid, String bssid) {
477 if (V) {
478 myLogV("onConnected: SSID: " + ssid + ", BSSID: " + bssid);
479 }
480
481 /*
482 * The current action being processed by the main watchdog thread is now
483 * stale, so cancel it.
484 */
485 cancelCurrentAction();
486
487 if ((mSsid == null) || !mSsid.equals(ssid)) {
488 /*
489 * This is a different network than what the main watchdog thread is
490 * processing, dispatch the network change message on the main thread.
491 */
492 mHandler.dispatchNetworkChanged(ssid);
493 }
494
495 if (requiresWatchdog(ssid, bssid)) {
496 if (D) {
497 myLogD(ssid + " (" + bssid + ") requires the watchdog");
498 }
499
500 // This access point requires a watchdog, so queue the check on the main thread
501 mHandler.checkAp(new AccessPoint(ssid, bssid));
502
503 } else {
504 if (D) {
505 myLogD(ssid + " (" + bssid + ") does not require the watchdog");
506 }
507
508 // This access point does not require a watchdog, so queue idle on the main thread
509 mHandler.idle();
510 }
511 }
512
513 /**
514 * Called when Wi-Fi is enabled.
515 */
516 private void onEnabled() {
517 cancelCurrentAction();
518 // Queue a hard-reset of the state on the main thread
519 mHandler.reset();
520 }
521
522 /**
523 * Called when disconnected (or some other event similar to being disconnected).
524 */
525 private void onDisconnected() {
526 if (V) {
527 myLogV("onDisconnected");
528 }
529
530 /*
531 * Disconnected from an access point, the action being processed by the
532 * watchdog thread is now stale, so cancel it.
533 */
534 cancelCurrentAction();
535 // Dispatch the disconnected to the main watchdog thread
536 mHandler.dispatchDisconnected();
537 // Queue the action to go idle
538 mHandler.idle();
539 }
540
541 /**
542 * Checks whether an access point requires watchdog monitoring.
543 *
544 * @param ssid The SSID of the access point.
545 * @param bssid The BSSID of the access point.
546 * @return Whether the access point/network should be monitored by the
547 * watchdog.
548 */
549 private boolean requiresWatchdog(String ssid, String bssid) {
550 if (V) {
551 myLogV("requiresWatchdog: SSID: " + ssid + ", BSSID: " + bssid);
552 }
553
554 WifiInfo info = null;
555 if (ssid == null) {
556 /*
557 * This is called from a Wi-Fi callback, so assume the WifiInfo does
558 * not have stale data.
559 */
560 info = mWifiManager.getConnectionInfo();
561 ssid = info.getSSID();
562 if (ssid == null) {
563 // It's still null, give up
564 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800565 Slog.v(TAG, " Invalid SSID, returning false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 }
567 return false;
568 }
569 }
570
571 if (TextUtils.isEmpty(bssid)) {
572 // Similar as above
573 if (info == null) {
574 info = mWifiManager.getConnectionInfo();
575 }
576 bssid = info.getBSSID();
577 if (TextUtils.isEmpty(bssid)) {
578 // It's still null, give up
579 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800580 Slog.v(TAG, " Invalid BSSID, returning false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
582 return false;
583 }
584 }
585
586 if (!isOnWatchList(ssid)) {
587 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800588 Slog.v(TAG, " SSID not on watch list, returning false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590 return false;
591 }
592
593 // The watchdog only monitors networks with multiple APs
594 if (!hasRequiredNumberOfAps(ssid)) {
595 return false;
596 }
597
598 return true;
599 }
600
601 private boolean isOnWatchList(String ssid) {
602 String watchList;
603
604 if (ssid == null || (watchList = getWatchList()) == null) {
605 return false;
606 }
607
608 String[] list = watchList.split(" *, *");
609
610 for (String name : list) {
611 if (ssid.equals(name)) {
612 return true;
613 }
614 }
615
616 return false;
617 }
618
619 /**
620 * Checks if the current scan results have multiple access points with an SSID.
621 *
622 * @param ssid The SSID to check.
623 * @return Whether the SSID has multiple access points.
624 */
625 private boolean hasRequiredNumberOfAps(String ssid) {
626 List<ScanResult> results = mWifiManager.getScanResults();
627 if (results == null) {
628 if (V) {
629 myLogV("hasRequiredNumberOfAps: Got null scan results, returning false");
630 }
631 return false;
632 }
633
634 int numApsRequired = getApCount();
635 int numApsFound = 0;
636 int resultsSize = results.size();
637 for (int i = 0; i < resultsSize; i++) {
638 ScanResult result = results.get(i);
639 if (result == null) continue;
640 if (result.SSID == null) continue;
641
642 if (result.SSID.equals(ssid)) {
643 numApsFound++;
644
645 if (numApsFound >= numApsRequired) {
646 if (V) {
647 myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning true");
648 }
649 return true;
650 }
651 }
652 }
653
654 if (V) {
655 myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning false");
656 }
657 return false;
658 }
659
660 // Watchdog logic (assume all of these methods will be in our main thread)
661
662 /**
663 * Handles a Wi-Fi network change (for example, from networkA to networkB).
664 */
665 private void handleNetworkChanged(String ssid) {
666 // Set the SSID being monitored to the new SSID
667 mSsid = ssid;
668 // Set various state to that when being idle
669 setIdleState(true);
670 }
671
672 /**
673 * Handles checking whether an AP is a "good" AP. If not, it will be blacklisted.
674 *
675 * @param ap The access point to check.
676 */
677 private void handleCheckAp(AccessPoint ap) {
678 // Reset the cancel state since this is the entry point of this action
679 mShouldCancel = false;
680
681 if (V) {
682 myLogV("handleCheckAp: AccessPoint: " + ap);
683 }
684
685 // Make sure we are not sleeping
686 if (mState == WatchdogState.SLEEP) {
687 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800688 Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 }
690 return;
691 }
692
693 mState = WatchdogState.CHECKING_AP;
694
695 /*
696 * Checks to make sure we haven't exceeded the max number of checks
697 * we're allowed per network
698 */
699 mNumApsChecked++;
700 if (mNumApsChecked > getMaxApChecks()) {
701 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800702 Slog.v(TAG, " Passed the max attempts (" + getMaxApChecks()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 + "), going to sleep for " + mSsid);
704 }
705 mHandler.sleep(mSsid);
706 return;
707 }
708
709 // Do the check
710 boolean isApAlive = checkDnsConnectivity();
711
712 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800713 Slog.v(TAG, " Is it alive: " + isApAlive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 }
715
716 // Take action based on results
717 if (isApAlive) {
718 handleApAlive(ap);
719 } else {
720 handleApUnresponsive(ap);
721 }
722 }
723
724 /**
725 * Handles the case when an access point is alive.
726 *
727 * @param ap The access point.
728 */
729 private void handleApAlive(AccessPoint ap) {
730 // Check whether we are stale and should cancel
731 if (shouldCancel()) return;
732 // We're satisfied with this AP, so go idle
733 setIdleState(false);
734
735 if (D) {
736 myLogD("AP is alive: " + ap.toString());
737 }
738
739 // Queue the next action to be a background check
740 mHandler.backgroundCheckAp(ap);
741 }
742
743 /**
744 * Handles an unresponsive AP by blacklisting it.
745 *
746 * @param ap The access point.
747 */
748 private void handleApUnresponsive(AccessPoint ap) {
749 // Check whether we are stale and should cancel
750 if (shouldCancel()) return;
751 // This AP is "bad", switch to another
752 mState = WatchdogState.SWITCHING_AP;
753
754 if (D) {
755 myLogD("AP is dead: " + ap.toString());
756 }
757
758 // Black list this "bad" AP, this will cause an attempt to connect to another
759 blacklistAp(ap.bssid);
Irfan Sheriff0049a1b2010-01-14 12:37:49 -0800760 // Initiate an association to an alternate AP
Irfan Sheriff0d255342010-07-28 09:35:20 -0700761 mWifiManager.reassociate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 }
763
764 private void blacklistAp(String bssid) {
765 if (TextUtils.isEmpty(bssid)) {
766 return;
767 }
768
769 // Before taking action, make sure we should not cancel our processing
770 if (shouldCancel()) return;
771
Irfan Sheriff0d255342010-07-28 09:35:20 -0700772 mWifiManager.addToBlacklist(bssid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773
774 if (D) {
775 myLogD("Blacklisting " + bssid);
776 }
777 }
778
779 /**
780 * Handles a single background check. If it fails, it should trigger a
781 * normal check. If it succeeds, it should queue another background check.
782 *
783 * @param ap The access point to do a background check for. If this is no
784 * longer the current AP, it is okay to return without any
785 * processing.
786 */
787 private void handleBackgroundCheckAp(AccessPoint ap) {
788 // Reset the cancel state since this is the entry point of this action
789 mShouldCancel = false;
790
791 if (false && V) {
792 myLogV("handleBackgroundCheckAp: AccessPoint: " + ap);
793 }
794
795 // Make sure we are not sleeping
796 if (mState == WatchdogState.SLEEP) {
797 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800798 Slog.v(TAG, " handleBackgroundCheckAp: Sleeping (in " + mSsid + "), so returning");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
800 return;
801 }
802
803 // Make sure the AP we're supposed to be background checking is still the active one
804 WifiInfo info = mWifiManager.getConnectionInfo();
805 if (info.getSSID() == null || !info.getSSID().equals(ap.ssid)) {
806 if (V) {
807 myLogV("handleBackgroundCheckAp: We are no longer connected to "
808 + ap + ", and instead are on " + info);
809 }
810 return;
811 }
812
813 if (info.getBSSID() == null || !info.getBSSID().equals(ap.bssid)) {
814 if (V) {
815 myLogV("handleBackgroundCheckAp: We are no longer connected to "
816 + ap + ", and instead are on " + info);
817 }
818 return;
819 }
820
821 // Do the check
822 boolean isApAlive = backgroundCheckDnsConnectivity();
823
824 if (V && !isApAlive) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800825 Slog.v(TAG, " handleBackgroundCheckAp: Is it alive: " + isApAlive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 }
827
828 if (shouldCancel()) {
829 return;
830 }
831
832 // Take action based on results
833 if (isApAlive) {
834 // Queue another background check
835 mHandler.backgroundCheckAp(ap);
836
837 } else {
838 if (D) {
839 myLogD("Background check failed for " + ap.toString());
840 }
841
842 // Queue a normal check, so it can take proper action
843 mHandler.checkAp(ap);
844 }
845 }
846
847 /**
848 * Handles going to sleep for this network. Going to sleep means we will not
849 * monitor this network anymore.
850 *
851 * @param ssid The network that will not be monitored anymore.
852 */
853 private void handleSleep(String ssid) {
854 // Make sure the network we're trying to sleep in is still the current network
855 if (ssid != null && ssid.equals(mSsid)) {
856 mState = WatchdogState.SLEEP;
857
858 if (D) {
859 myLogD("Going to sleep for " + ssid);
860 }
861
862 /*
863 * Before deciding to go to sleep, we may have checked a few APs
864 * (and blacklisted them). Clear the blacklist so the AP with best
865 * signal is chosen.
866 */
Irfan Sheriff0d255342010-07-28 09:35:20 -0700867 mWifiManager.clearBlacklist();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868
869 if (V) {
870 myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
871 }
872 }
873 }
874
875 /**
876 * Handles an access point disconnection.
877 */
878 private void handleDisconnected() {
879 /*
880 * We purposefully do not change mSsid to null. This is to handle
881 * disconnected followed by connected better (even if there is some
882 * duration in between). For example, if the watchdog went to sleep in a
883 * network, and then the phone goes to sleep, when the phone wakes up we
884 * still want to be in the sleeping state. When the phone went to sleep,
885 * we would have gotten a disconnected event which would then set mSsid
886 * = null. This is bad, since the following connect would cause us to do
887 * the "network is good?" check all over again. */
888
889 /*
890 * Set the state as if we were idle (don't come out of sleep, only
891 * hard reset and network changed should do that.
892 */
893 setIdleState(false);
894 }
895
896 /**
897 * Handles going idle. Idle means we are satisfied with the current state of
898 * things, but if a new connection occurs we'll re-evaluate.
899 */
900 private void handleIdle() {
901 // Reset the cancel state since this is the entry point for this action
902 mShouldCancel = false;
903
904 if (V) {
905 myLogV("handleSwitchToIdle");
906 }
907
908 // If we're sleeping, don't do anything
909 if (mState == WatchdogState.SLEEP) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800910 Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 return;
912 }
913
914 // Set the idle state
915 setIdleState(false);
916
917 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800918 Slog.v(TAG, " Set state to IDLE");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
920 }
921
922 /**
923 * Sets the state as if we are going idle.
924 */
925 private void setIdleState(boolean forceIdleState) {
926 // Setting idle state does not kick us out of sleep unless the forceIdleState is set
927 if (forceIdleState || (mState != WatchdogState.SLEEP)) {
928 mState = WatchdogState.IDLE;
929 }
930 mNumApsChecked = 0;
931 }
932
933 /**
934 * Handles a hard reset. A hard reset is rarely used, but when used it
935 * should revert anything done by the watchdog monitoring.
936 */
937 private void handleReset() {
Irfan Sheriff0d255342010-07-28 09:35:20 -0700938 mWifiManager.clearBlacklist();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 setIdleState(true);
940 }
941
942 // Inner classes
943
944 /**
945 * Possible states for the watchdog to be in.
946 */
947 private static enum WatchdogState {
948 /** The watchdog is currently idle, but it is still responsive to future AP checks in this network. */
949 IDLE,
950 /** The watchdog is sleeping, so it will not try any AP checks for the network. */
951 SLEEP,
952 /** The watchdog is currently checking an AP for connectivity. */
953 CHECKING_AP,
954 /** The watchdog is switching to another AP in the network. */
955 SWITCHING_AP
956 }
957
958 /**
959 * The main thread for the watchdog monitoring. This will be turned into a
960 * {@link Looper} thread.
961 */
962 private class WifiWatchdogThread extends Thread {
963 WifiWatchdogThread() {
964 super("WifiWatchdogThread");
965 }
966
967 @Override
968 public void run() {
969 // Set this thread up so the handler will work on it
970 Looper.prepare();
971
972 synchronized(WifiWatchdogService.this) {
973 mHandler = new WifiWatchdogHandler();
974
975 // Notify that the handler has been created
976 WifiWatchdogService.this.notify();
977 }
978
979 // Listen for messages to the handler
980 Looper.loop();
981 }
982 }
983
984 /**
985 * The main thread's handler. There are 'actions', and just general
986 * 'messages'. There should only ever be one 'action' in the queue (aside
987 * from the one being processed, if any). There may be multiple messages in
988 * the queue. So, actions are replaced by more recent actions, where as
989 * messages will be executed for sure. Messages end up being used to just
990 * change some state, and not really take any action.
991 * <p>
992 * There is little logic inside this class, instead methods of the form
993 * "handle___" are called in the main {@link WifiWatchdogService}.
994 */
995 private class WifiWatchdogHandler extends Handler {
996 /** Check whether the AP is "good". The object will be an {@link AccessPoint}. */
997 static final int ACTION_CHECK_AP = 1;
998 /** Go into the idle state. */
999 static final int ACTION_IDLE = 2;
1000 /**
1001 * Performs a periodic background check whether the AP is still "good".
1002 * The object will be an {@link AccessPoint}.
1003 */
1004 static final int ACTION_BACKGROUND_CHECK_AP = 3;
1005
1006 /**
1007 * Go to sleep for the current network. We are conservative with making
1008 * this a message rather than action. We want to make sure our main
1009 * thread sees this message, but if it were an action it could be
1010 * removed from the queue and replaced by another action. The main
1011 * thread will ensure when it sees the message that the state is still
1012 * valid for going to sleep.
1013 * <p>
1014 * For an explanation of sleep, see {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}.
1015 */
1016 static final int MESSAGE_SLEEP = 101;
1017 /** Disables the watchdog. */
1018 static final int MESSAGE_DISABLE_WATCHDOG = 102;
1019 /** The network has changed. */
1020 static final int MESSAGE_NETWORK_CHANGED = 103;
1021 /** The current access point has disconnected. */
1022 static final int MESSAGE_DISCONNECTED = 104;
1023 /** Performs a hard-reset on the watchdog state. */
1024 static final int MESSAGE_RESET = 105;
1025
1026 void checkAp(AccessPoint ap) {
1027 removeAllActions();
1028 sendMessage(obtainMessage(ACTION_CHECK_AP, ap));
1029 }
1030
1031 void backgroundCheckAp(AccessPoint ap) {
1032 if (!isBackgroundCheckEnabled()) return;
1033
1034 removeAllActions();
1035 sendMessageDelayed(obtainMessage(ACTION_BACKGROUND_CHECK_AP, ap),
1036 getBackgroundCheckDelayMs());
1037 }
1038
1039 void idle() {
1040 removeAllActions();
1041 sendMessage(obtainMessage(ACTION_IDLE));
1042 }
1043
1044 void sleep(String ssid) {
1045 removeAllActions();
1046 sendMessage(obtainMessage(MESSAGE_SLEEP, ssid));
1047 }
1048
1049 void disableWatchdog() {
1050 removeAllActions();
1051 sendMessage(obtainMessage(MESSAGE_DISABLE_WATCHDOG));
1052 }
1053
1054 void dispatchNetworkChanged(String ssid) {
1055 removeAllActions();
1056 sendMessage(obtainMessage(MESSAGE_NETWORK_CHANGED, ssid));
1057 }
1058
1059 void dispatchDisconnected() {
1060 removeAllActions();
1061 sendMessage(obtainMessage(MESSAGE_DISCONNECTED));
1062 }
1063
1064 void reset() {
1065 removeAllActions();
1066 sendMessage(obtainMessage(MESSAGE_RESET));
1067 }
1068
1069 private void removeAllActions() {
1070 removeMessages(ACTION_CHECK_AP);
1071 removeMessages(ACTION_IDLE);
1072 removeMessages(ACTION_BACKGROUND_CHECK_AP);
1073 }
1074
1075 @Override
1076 public void handleMessage(Message msg) {
1077 switch (msg.what) {
1078 case MESSAGE_NETWORK_CHANGED:
1079 handleNetworkChanged((String) msg.obj);
1080 break;
1081 case ACTION_CHECK_AP:
1082 handleCheckAp((AccessPoint) msg.obj);
1083 break;
1084 case ACTION_BACKGROUND_CHECK_AP:
1085 handleBackgroundCheckAp((AccessPoint) msg.obj);
1086 break;
1087 case MESSAGE_SLEEP:
1088 handleSleep((String) msg.obj);
1089 break;
1090 case ACTION_IDLE:
1091 handleIdle();
1092 break;
1093 case MESSAGE_DISABLE_WATCHDOG:
1094 handleIdle();
1095 break;
1096 case MESSAGE_DISCONNECTED:
1097 handleDisconnected();
1098 break;
1099 case MESSAGE_RESET:
1100 handleReset();
1101 break;
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Receives Wi-Fi broadcasts.
1108 * <p>
1109 * There is little logic in this class, instead methods of the form "on___"
1110 * are called in the {@link WifiWatchdogService}.
1111 */
1112 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
1113
1114 @Override
1115 public void onReceive(Context context, Intent intent) {
1116 final String action = intent.getAction();
1117 if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
1118 handleNetworkStateChanged(
1119 (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1121 handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1122 WifiManager.WIFI_STATE_UNKNOWN));
1123 }
1124 }
1125
1126 private void handleNetworkStateChanged(NetworkInfo info) {
1127 if (V) {
1128 myLogV("Receiver.handleNetworkStateChanged: NetworkInfo: "
1129 + info);
1130 }
1131
1132 switch (info.getState()) {
1133 case CONNECTED:
1134 WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
1135 if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
1136 if (V) {
1137 myLogV("handleNetworkStateChanged: Got connected event but SSID or BSSID are null. SSID: "
1138 + wifiInfo.getSSID()
1139 + ", BSSID: "
1140 + wifiInfo.getBSSID() + ", ignoring event");
1141 }
1142 return;
1143 }
1144 onConnected(wifiInfo.getSSID(), wifiInfo.getBSSID());
1145 break;
1146
1147 case DISCONNECTED:
1148 onDisconnected();
1149 break;
1150 }
1151 }
1152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 private void handleWifiStateChanged(int wifiState) {
1154 if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
Irfan Sheriffa2a1b912010-06-07 09:03:04 -07001155 onDisconnected();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
1157 onEnabled();
1158 }
1159 }
1160 };
1161
1162 /**
1163 * Describes an access point by its SSID and BSSID.
1164 */
1165 private static class AccessPoint {
1166 String ssid;
1167 String bssid;
1168
1169 AccessPoint(String ssid, String bssid) {
1170 this.ssid = ssid;
1171 this.bssid = bssid;
1172 }
1173
1174 private boolean hasNull() {
1175 return ssid == null || bssid == null;
1176 }
1177
1178 @Override
1179 public boolean equals(Object o) {
1180 if (!(o instanceof AccessPoint)) return false;
1181 AccessPoint otherAp = (AccessPoint) o;
1182 boolean iHaveNull = hasNull();
1183 // Either we both have a null, or our SSIDs and BSSIDs are equal
1184 return (iHaveNull && otherAp.hasNull()) ||
1185 (otherAp.bssid != null && ssid.equals(otherAp.ssid)
1186 && bssid.equals(otherAp.bssid));
1187 }
1188
1189 @Override
1190 public int hashCode() {
1191 if (ssid == null || bssid == null) return 0;
1192 return ssid.hashCode() + bssid.hashCode();
1193 }
1194
1195 @Override
1196 public String toString() {
1197 return ssid + " (" + bssid + ")";
1198 }
1199 }
1200
1201 /**
1202 * Performs a simple DNS "ping" by sending a "server status" query packet to
1203 * the DNS server. As long as the server replies, we consider it a success.
1204 * <p>
1205 * We do not use a simple hostname lookup because that could be cached and
1206 * the API may not differentiate between a time out and a failure lookup
1207 * (which we really care about).
1208 */
1209 private static class DnsPinger {
1210
1211 /** Number of bytes for the query */
1212 private static final int DNS_QUERY_BASE_SIZE = 33;
1213
1214 /** The DNS port */
1215 private static final int DNS_PORT = 53;
1216
1217 /** Used to generate IDs */
1218 private static Random sRandom = new Random();
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001219
1220 static boolean isDnsReachable(InetAddress dnsAddress, int timeout) {
Nick Kralevich929b4852010-06-09 14:27:43 -07001221 DatagramSocket socket = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 try {
Nick Kralevich929b4852010-06-09 14:27:43 -07001223 socket = new DatagramSocket();
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 // Set some socket properties
1226 socket.setSoTimeout(timeout);
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 byte[] buf = new byte[DNS_QUERY_BASE_SIZE];
1229 fillQuery(buf);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001231 // Send the DNS query
1232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 DatagramPacket packet = new DatagramPacket(buf,
1234 buf.length, dnsAddress, DNS_PORT);
1235 socket.send(packet);
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 // Wait for reply (blocks for the above timeout)
1238 DatagramPacket replyPacket = new DatagramPacket(buf, buf.length);
1239 socket.receive(replyPacket);
1240
1241 // If a timeout occurred, an exception would have been thrown. We got a reply!
1242 return true;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 } catch (SocketException e) {
1245 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001246 Slog.v(TAG, "DnsPinger.isReachable received SocketException", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
1248 return false;
Robert Greenwalt8e9abc52011-02-16 10:37:50 -08001249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 } catch (UnknownHostException e) {
1251 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001252 Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254 return false;
1255
1256 } catch (SocketTimeoutException e) {
1257 return false;
1258
1259 } catch (IOException e) {
1260 if (V) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001261 Slog.v(TAG, "DnsPinger.isReachable got an IOException", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 }
1263 return false;
1264
1265 } catch (Exception e) {
Joe Onorato43a17652011-04-06 19:22:23 -07001266 if (V || false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001267 Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 }
1269 return false;
Nick Kralevich929b4852010-06-09 14:27:43 -07001270 } finally {
1271 if (socket != null) {
1272 socket.close();
1273 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 }
1275 }
1276
1277 private static void fillQuery(byte[] buf) {
1278
1279 /*
1280 * See RFC2929 (though the bit tables in there are misleading for
1281 * us. For example, the recursion desired bit is the 0th bit for us,
1282 * but looking there it would appear as the 7th bit of the byte
1283 */
1284
1285 // Make sure it's all zeroed out
1286 for (int i = 0; i < buf.length; i++) buf[i] = 0;
1287
1288 // Form a query for www.android.com
1289
1290 // [0-1] bytes are an ID, generate random ID for this query
1291 buf[0] = (byte) sRandom.nextInt(256);
1292 buf[1] = (byte) sRandom.nextInt(256);
1293
1294 // [2-3] bytes are for flags.
1295 buf[2] = 1; // Recursion desired
1296
1297 // [4-5] bytes are for the query count
1298 buf[5] = 1; // One query
1299
1300 // [6-7] [8-9] [10-11] are all counts of other fields we don't use
1301
1302 // [12-15] for www
1303 writeString(buf, 12, "www");
1304
1305 // [16-23] for android
1306 writeString(buf, 16, "android");
1307
1308 // [24-27] for com
1309 writeString(buf, 24, "com");
1310
1311 // [29-30] bytes are for QTYPE, set to 1
1312 buf[30] = 1;
1313
1314 // [31-32] bytes are for QCLASS, set to 1
1315 buf[32] = 1;
1316 }
1317
1318 private static void writeString(byte[] buf, int startPos, String string) {
1319 int pos = startPos;
1320
1321 // Write the length first
1322 buf[pos++] = (byte) string.length();
1323 for (int i = 0; i < string.length(); i++) {
1324 buf[pos++] = (byte) string.charAt(i);
1325 }
1326 }
1327 }
1328}