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