summaryrefslogtreecommitdiffstats
path: root/wifi/java
diff options
context:
space:
mode:
authorIsaac Levy <ilevy@google.com>2011-07-06 13:54:48 -0700
committerIsaac Levy <ilevy@google.com>2011-07-06 13:54:48 -0700
commita7bc1135c270fd4a84ab7ad45b7194e9b580300e (patch)
treeccde642a2b28cb3fc18469a1cdd0b9187e87c2fd /wifi/java
parente58e9b8c8297dc9ae407c132d0f0983e63be46f0 (diff)
downloadframeworks_base-a7bc1135c270fd4a84ab7ad45b7194e9b580300e.zip
frameworks_base-a7bc1135c270fd4a84ab7ad45b7194e9b580300e.tar.gz
frameworks_base-a7bc1135c270fd4a84ab7ad45b7194e9b580300e.tar.bz2
Moved WifiWatchdogService and DnsPinger
- Moved WWS and DnsPinger in prep for StateMachine rewrite intent. Change-Id: I912ca80d31ee2ded99eb345a2e5a55d01bd9910c
Diffstat (limited to 'wifi/java')
-rw-r--r--wifi/java/android/net/wifi/WifiWatchdogService.java765
1 files changed, 765 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/WifiWatchdogService.java b/wifi/java/android/net/wifi/WifiWatchdogService.java
new file mode 100644
index 0000000..bce4b3a
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiWatchdogService.java
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.DnsPinger;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
+ * network with multiple access points. After the framework successfully
+ * connects to an access point, the watchdog verifies connectivity by 'pinging'
+ * the configured DNS server using {@link DnsPinger}.
+ * <p>
+ * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
+ * that another AP might have internet access; otherwise the SSID is disabled.
+ * <p>
+ * On DNS success, the WatchdogService initiates a walled garden check via an
+ * http get. A browser windows is activated if a walled garden is detected.
+ *
+ * @hide
+ */
+public class WifiWatchdogService {
+
+ private static final String WWS_TAG = "WifiWatchdogService";
+
+ private static final boolean VDBG = true;
+ private static final boolean DBG = true;
+
+ // Used for verbose logging
+ private String mDNSCheckLogStr;
+
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private WifiManager mWifiManager;
+
+ private WifiWatchdogHandler mHandler;
+
+ private DnsPinger mDnsPinger;
+
+ private IntentFilter mIntentFilter;
+ private BroadcastReceiver mBroadcastReceiver;
+ private boolean mBroadcastsEnabled;
+
+ private static final int WIFI_SIGNAL_LEVELS = 4;
+
+ /**
+ * Low signal is defined as less than or equal to cut off
+ */
+ private static final int LOW_SIGNAL_CUTOFF = 0;
+
+ private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL = 2 * 60 * 1000;
+ private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000;
+ private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000;
+
+ private static final int MAX_CHECKS_PER_SSID = 9;
+ private static final int NUM_DNS_PINGS = 7;
+ private static double MIN_RESPONSE_RATE = 0.50;
+
+ // TODO : Adjust multiple DNS downward to 250 on repeated failure
+ // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250;
+
+ private static final int DNS_PING_TIMEOUT_MS = 800;
+ private static final long DNS_PING_INTERVAL = 250;
+
+ private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000;
+
+ private Status mStatus = new Status();
+
+ private static class Status {
+ String bssid = "";
+ String ssid = "";
+
+ HashSet<String> allBssids = new HashSet<String>();
+ int numFullDNSchecks = 0;
+
+ long lastSingleCheckTime = -24 * 60 * 60 * 1000;
+ long lastWalledGardenCheckTime = -24 * 60 * 60 * 1000;
+
+ WatchdogState state = WatchdogState.INACTIVE;
+
+ // Info for dns check
+ int dnsCheckTries = 0;
+ int dnsCheckSuccesses = 0;
+
+ public int signal = -200;
+
+ }
+
+ private enum WatchdogState {
+ /**
+ * Full DNS check in progress
+ */
+ DNS_FULL_CHECK,
+
+ /**
+ * Walled Garden detected, will pop up browser next round.
+ */
+ WALLED_GARDEN_DETECTED,
+
+ /**
+ * DNS failed, will blacklist/disable AP next round
+ */
+ DNS_CHECK_FAILURE,
+
+ /**
+ * Online or displaying walled garden auth page
+ */
+ CHECKS_COMPLETE,
+
+ /**
+ * Watchdog idle, network has been blacklisted or received disconnect
+ * msg
+ */
+ INACTIVE,
+
+ BLACKLISTED_AP
+ }
+
+ public WifiWatchdogService(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context,
+ ConnectivityManager.TYPE_WIFI);
+
+ HandlerThread handlerThread = new HandlerThread("WifiWatchdogServiceThread");
+ handlerThread.start();
+ mHandler = new WifiWatchdogHandler(handlerThread.getLooper());
+
+ setupNetworkReceiver();
+
+ // The content observer to listen needs a handler, which createThread
+ // creates
+ registerForSettingsChanges();
+
+ // Start things off
+ if (isWatchdogEnabled()) {
+ mHandler.sendEmptyMessage(WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT);
+ }
+ }
+
+ /**
+ *
+ */
+ private void setupNetworkReceiver() {
+ mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ WifiWatchdogHandler.MESSAGE_NETWORK_EVENT,
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)
+ ));
+ } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ mHandler.sendEmptyMessage(WifiWatchdogHandler.RSSI_CHANGE_EVENT);
+ } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ mHandler.sendEmptyMessage(WifiWatchdogHandler.SCAN_RESULTS_AVAILABLE);
+ } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ WifiWatchdogHandler.WIFI_STATE_CHANGE,
+ intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 4)));
+ }
+ }
+ };
+
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ }
+
+ /**
+ * Observes the watchdog on/off setting, and takes action when changed.
+ */
+ private void registerForSettingsChanges() {
+ ContentObserver contentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.sendEmptyMessage((WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT));
+ }
+ };
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
+ false, contentObserver);
+ }
+
+ private void handleNewConnection() {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ String newSsid = wifiInfo.getSSID();
+ String newBssid = wifiInfo.getBSSID();
+
+ if (VDBG) {
+ Slog.v(WWS_TAG, String.format("handleConnected:: old (%s, %s) ==> new (%s, %s)",
+ mStatus.ssid, mStatus.bssid, newSsid, newBssid));
+ }
+
+ if (TextUtils.isEmpty(newSsid) || TextUtils.isEmpty(newBssid)) {
+ return;
+ }
+
+ if (!TextUtils.equals(mStatus.ssid, newSsid)) {
+ mStatus = new Status();
+ mStatus.ssid = newSsid;
+ }
+
+ mStatus.bssid = newBssid;
+ mStatus.allBssids.add(newBssid);
+ mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS);
+
+ initDnsFullCheck();
+ }
+
+ public void updateRssi() {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (!TextUtils.equals(mStatus.ssid, wifiInfo.getSSID()) ||
+ !TextUtils.equals(mStatus.bssid, wifiInfo.getBSSID())) {
+ return;
+ }
+
+ mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS);
+ }
+
+ /**
+ * Single step in state machine
+ */
+ private void handleStateStep() {
+ // Slog.v(WWS_TAG, "handleStateStep:: " + mStatus.state);
+
+ switch (mStatus.state) {
+ case DNS_FULL_CHECK:
+ if (VDBG) {
+ Slog.v(WWS_TAG, "DNS_FULL_CHECK: " + mDNSCheckLogStr);
+ }
+
+ long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
+ DNS_PING_TIMEOUT_MS);
+
+ mStatus.dnsCheckTries++;
+ if (pingResponseTime >= 0)
+ mStatus.dnsCheckSuccesses++;
+
+ if (DBG) {
+ if (pingResponseTime >= 0) {
+ mDNSCheckLogStr += " | " + pingResponseTime;
+ } else {
+ mDNSCheckLogStr += " | " + "x";
+ }
+ }
+
+ switch (currentDnsCheckStatus()) {
+ case SUCCESS:
+ if (DBG) {
+ Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Success");
+ }
+ doWalledGardenCheck();
+ break;
+ case FAILURE:
+ if (DBG) {
+ Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Failure");
+ }
+ mStatus.state = WatchdogState.DNS_CHECK_FAILURE;
+ break;
+ case INCOMPLETE:
+ // Taking no action
+ break;
+ }
+ break;
+ case DNS_CHECK_FAILURE:
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (!mStatus.ssid.equals(wifiInfo.getSSID()) ||
+ !mStatus.bssid.equals(wifiInfo.getBSSID())) {
+ Slog.i(WWS_TAG, "handleState DNS_CHECK_FAILURE:: network has changed!");
+ mStatus.state = WatchdogState.INACTIVE;
+ break;
+ }
+
+ if (mStatus.numFullDNSchecks >= mStatus.allBssids.size() ||
+ mStatus.numFullDNSchecks >= MAX_CHECKS_PER_SSID) {
+ disableAP(wifiInfo);
+ } else {
+ blacklistAP();
+ }
+ break;
+ case WALLED_GARDEN_DETECTED:
+ popUpBrowser();
+ mStatus.state = WatchdogState.CHECKS_COMPLETE;
+ break;
+ case BLACKLISTED_AP:
+ WifiInfo wifiInfo2 = mWifiManager.getConnectionInfo();
+ if (wifiInfo2.getSupplicantState() != SupplicantState.COMPLETED) {
+ Slog.d(WWS_TAG,
+ "handleState::BlacklistedAP - offline, but didn't get disconnect!");
+ mStatus.state = WatchdogState.INACTIVE;
+ break;
+ }
+ if (mStatus.bssid.equals(wifiInfo2.getBSSID())) {
+ Slog.d(WWS_TAG, "handleState::BlacklistedAP - connected to same bssid");
+ if (!handleSingleDnsCheck()) {
+ disableAP(wifiInfo2);
+ break;
+ }
+ }
+
+ Slog.d(WWS_TAG, "handleState::BlacklistedAP - Simiulating a new connection");
+ handleNewConnection();
+ break;
+ }
+ }
+
+ private void doWalledGardenCheck() {
+ if (!isWalledGardenTestEnabled()) {
+ if (VDBG)
+ Slog.v(WWS_TAG, "Skipping walled garden check - disabled");
+ mStatus.state = WatchdogState.CHECKS_COMPLETE;
+ return;
+ }
+ long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL,
+ mStatus.lastWalledGardenCheckTime);
+ if (waitTime > 0) {
+ if (VDBG) {
+ Slog.v(WWS_TAG, "Skipping walled garden check - wait " +
+ waitTime + " ms.");
+ }
+ mStatus.state = WatchdogState.CHECKS_COMPLETE;
+ return;
+ }
+
+ mStatus.lastWalledGardenCheckTime = SystemClock.elapsedRealtime();
+ if (isWalledGardenConnection()) {
+ if (DBG)
+ Slog.d(WWS_TAG,
+ "Walled garden test complete - walled garden detected");
+ mStatus.state = WatchdogState.WALLED_GARDEN_DETECTED;
+ } else {
+ if (DBG)
+ Slog.d(WWS_TAG, "Walled garden test complete - online");
+ mStatus.state = WatchdogState.CHECKS_COMPLETE;
+ }
+ }
+
+ private boolean handleSingleDnsCheck() {
+ mStatus.lastSingleCheckTime = SystemClock.elapsedRealtime();
+ long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
+ DNS_PING_TIMEOUT_MS);
+ if (DBG) {
+ Slog.d(WWS_TAG, "Ran a single DNS ping. Response time: " + responseTime);
+ }
+ if (responseTime < 0) {
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * @return Delay in MS before next single DNS check can proceed.
+ */
+ private long timeToNextScheduledDNSCheck() {
+ if (mStatus.signal > LOW_SIGNAL_CUTOFF) {
+ return waitTime(MIN_SINGLE_DNS_CHECK_INTERVAL, mStatus.lastSingleCheckTime);
+ } else {
+ return waitTime(MIN_LOW_SIGNAL_CHECK_INTERVAL, mStatus.lastSingleCheckTime);
+ }
+ }
+
+ /**
+ * Helper to return wait time left given a min interval and last run
+ *
+ * @param interval minimum wait interval
+ * @param lastTime last time action was performed in
+ * SystemClock.elapsedRealtime()
+ * @return non negative time to wait
+ */
+ private static long waitTime(long interval, long lastTime) {
+ long wait = interval + lastTime - SystemClock.elapsedRealtime();
+ return wait > 0 ? wait : 0;
+ }
+
+ private void popUpBrowser() {
+ Uri uri = Uri.parse("http://www.google.com");
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+
+ private void disableAP(WifiInfo info) {
+ // TODO : Unban networks if they had low signal ?
+ Slog.i(WWS_TAG, String.format("Disabling current SSID, %s [bssid %s]. " +
+ "numChecks %d, numAPs %d", mStatus.ssid, mStatus.bssid,
+ mStatus.numFullDNSchecks, mStatus.allBssids.size()));
+ mWifiManager.disableNetwork(info.getNetworkId());
+ mStatus.state = WatchdogState.INACTIVE;
+ }
+
+ private void blacklistAP() {
+ Slog.i(WWS_TAG, String.format("Blacklisting current BSSID %s [ssid %s]. " +
+ "numChecks %d, numAPs %d", mStatus.bssid, mStatus.ssid,
+ mStatus.numFullDNSchecks, mStatus.allBssids.size()));
+
+ mWifiManager.addToBlacklist(mStatus.bssid);
+ mWifiManager.reassociate();
+ mStatus.state = WatchdogState.BLACKLISTED_AP;
+ }
+
+ /**
+ * Checks the scan for new BBIDs using current mSsid
+ */
+ private void updateBssids() {
+ String curSsid = mStatus.ssid;
+ HashSet<String> bssids = mStatus.allBssids;
+ List<ScanResult> results = mWifiManager.getScanResults();
+ int oldNumBssids = bssids.size();
+
+ if (results == null) {
+ if (VDBG) {
+ Slog.v(WWS_TAG, "updateBssids: Got null scan results!");
+ }
+ return;
+ }
+
+ for (ScanResult result : results) {
+ if (result != null && curSsid.equals(result.SSID))
+ bssids.add(result.BSSID);
+ }
+
+ // if (VDBG && bssids.size() - oldNumBssids > 0) {
+ // Slog.v(WWS_TAG,
+ // String.format("updateBssids:: Found %d new APs (total %d) on SSID %s",
+ // bssids.size() - oldNumBssids, bssids.size(), curSsid));
+ // }
+ }
+
+ enum DnsCheckStatus {
+ SUCCESS,
+ FAILURE,
+ INCOMPLETE
+ }
+
+ /**
+ * Computes the current results of the dns check, ends early if outcome is
+ * assured.
+ */
+ private DnsCheckStatus currentDnsCheckStatus() {
+ /**
+ * After a full ping count, if we have more responses than this cutoff,
+ * the outcome is success; else it is 'failure'.
+ */
+ double pingResponseCutoff = MIN_RESPONSE_RATE * NUM_DNS_PINGS;
+ int remainingChecks = NUM_DNS_PINGS - mStatus.dnsCheckTries;
+
+ /**
+ * Our final success count will be at least this big, so we're
+ * guaranteed to succeed.
+ */
+ if (mStatus.dnsCheckSuccesses >= pingResponseCutoff) {
+ return DnsCheckStatus.SUCCESS;
+ }
+
+ /**
+ * Our final count will be at most the current count plus the remaining
+ * pings - we're guaranteed to fail.
+ */
+ if (remainingChecks + mStatus.dnsCheckSuccesses < pingResponseCutoff) {
+ return DnsCheckStatus.FAILURE;
+ }
+
+ return DnsCheckStatus.INCOMPLETE;
+ }
+
+ private void initDnsFullCheck() {
+ if (DBG) {
+ Slog.d(WWS_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
+ }
+ mStatus.numFullDNSchecks++;
+ mStatus.dnsCheckSuccesses = 0;
+ mStatus.dnsCheckTries = 0;
+ mStatus.state = WatchdogState.DNS_FULL_CHECK;
+
+ if (DBG) {
+ mDNSCheckLogStr = String.format("Dns Check %d. Pinging %s on ssid [%s]: ",
+ mStatus.numFullDNSchecks, mDnsPinger.getDns(),
+ mStatus.ssid);
+ }
+ }
+
+ /**
+ * DNS based detection techniques do not work at all hotspots. The one sure
+ * way to check a walled garden is to see if a URL fetch on a known address
+ * fetches the data we expect
+ */
+ private boolean isWalledGardenConnection() {
+ InputStream in = null;
+ HttpURLConnection urlConnection = null;
+ try {
+ URL url = new URL(getWalledGardenUrl());
+ urlConnection = (HttpURLConnection) url.openConnection();
+ in = new BufferedInputStream(urlConnection.getInputStream());
+ Scanner scanner = new Scanner(in);
+ if (scanner.findInLine(getWalledGardenPattern()) != null) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (IOException e) {
+ return false;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ if (urlConnection != null)
+ urlConnection.disconnect();
+ }
+ }
+
+ /**
+ * There is little logic inside this class, instead methods of the form
+ * "handle___" are called in the main {@link WifiWatchdogService}.
+ */
+ private class WifiWatchdogHandler extends Handler {
+ /**
+ * Major network event, object is NetworkInfo
+ */
+ static final int MESSAGE_NETWORK_EVENT = 1;
+ /**
+ * Change in settings, no object
+ */
+ static final int MESSAGE_CONTEXT_EVENT = 2;
+
+ /**
+ * Change in signal strength
+ */
+ static final int RSSI_CHANGE_EVENT = 3;
+ static final int SCAN_RESULTS_AVAILABLE = 4;
+
+ static final int WIFI_STATE_CHANGE = 5;
+
+ /**
+ * Single step of state machine. One DNS check, or one WalledGarden
+ * check, or one external action. We separate out external actions to
+ * increase chance of detecting that a check failure is caused by change
+ * in network status. Messages should have an arg1 which to sync status
+ * messages.
+ */
+ static final int CHECK_SEQUENCE_STEP = 10;
+ static final int SINGLE_DNS_CHECK = 11;
+
+ /**
+ * @param looper
+ */
+ public WifiWatchdogHandler(Looper looper) {
+ super(looper);
+ }
+
+ boolean singleCheckQueued = false;
+ long queuedSingleDnsCheckArrival;
+
+ /**
+ * Sends a singleDnsCheck message with shortest time - guards against
+ * multiple.
+ */
+ private boolean queueSingleDnsCheck() {
+ long delay = timeToNextScheduledDNSCheck();
+ long newArrival = delay + SystemClock.elapsedRealtime();
+ if (singleCheckQueued && queuedSingleDnsCheckArrival <= newArrival)
+ return true;
+ queuedSingleDnsCheckArrival = newArrival;
+ singleCheckQueued = true;
+ removeMessages(SINGLE_DNS_CHECK);
+ return sendMessageDelayed(obtainMessage(SINGLE_DNS_CHECK), delay);
+ }
+
+ boolean checkSequenceQueued = false;
+ long queuedCheckSequenceArrival;
+
+ /**
+ * Sends a state_machine_step message if the delay requested is lower
+ * than the current delay.
+ */
+ private boolean sendCheckSequenceStep(long delay) {
+ long newArrival = delay + SystemClock.elapsedRealtime();
+ if (checkSequenceQueued && queuedCheckSequenceArrival <= newArrival)
+ return true;
+ queuedCheckSequenceArrival = newArrival;
+ checkSequenceQueued = true;
+ removeMessages(CHECK_SEQUENCE_STEP);
+ return sendMessageDelayed(obtainMessage(CHECK_SEQUENCE_STEP), delay);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case CHECK_SEQUENCE_STEP:
+ checkSequenceQueued = false;
+ handleStateStep();
+ if (mStatus.state == WatchdogState.CHECKS_COMPLETE) {
+ queueSingleDnsCheck();
+ } else if (mStatus.state == WatchdogState.DNS_FULL_CHECK) {
+ sendCheckSequenceStep(DNS_PING_INTERVAL);
+ } else if (mStatus.state == WatchdogState.BLACKLISTED_AP) {
+ sendCheckSequenceStep(BLACKLIST_FOLLOWUP_INTERVAL);
+ } else if (mStatus.state != WatchdogState.INACTIVE) {
+ sendCheckSequenceStep(0);
+ }
+ return;
+ case MESSAGE_NETWORK_EVENT:
+ if (!mBroadcastsEnabled) {
+ Slog.e(WWS_TAG,
+ "MessageNetworkEvent - WatchdogService not enabled... returning");
+ return;
+ }
+ NetworkInfo info = (NetworkInfo) msg.obj;
+ switch (info.getState()) {
+ case DISCONNECTED:
+ mStatus.state = WatchdogState.INACTIVE;
+ return;
+ case CONNECTED:
+ handleNewConnection();
+ sendCheckSequenceStep(0);
+ }
+ return;
+ case SINGLE_DNS_CHECK:
+ singleCheckQueued = false;
+ if (mStatus.state != WatchdogState.CHECKS_COMPLETE) {
+ Slog.d(WWS_TAG, "Single check returning, curState: " + mStatus.state);
+ break;
+ }
+
+ if (!handleSingleDnsCheck()) {
+ initDnsFullCheck();
+ sendCheckSequenceStep(0);
+ } else {
+ queueSingleDnsCheck();
+ }
+
+ break;
+ case RSSI_CHANGE_EVENT:
+ updateRssi();
+ if (mStatus.state == WatchdogState.CHECKS_COMPLETE)
+ queueSingleDnsCheck();
+ break;
+ case SCAN_RESULTS_AVAILABLE:
+ updateBssids();
+ break;
+ case WIFI_STATE_CHANGE:
+ if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
+ Slog.i(WWS_TAG, "WifiStateDisabling -- Resetting WatchdogState");
+ mStatus = new Status();
+ }
+ break;
+ case MESSAGE_CONTEXT_EVENT:
+ if (isWatchdogEnabled() && !mBroadcastsEnabled) {
+ mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
+ mBroadcastsEnabled = true;
+ Slog.i(WWS_TAG, "WifiWatchdogService enabled");
+ } else if (!isWatchdogEnabled() && mBroadcastsEnabled) {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ removeMessages(SINGLE_DNS_CHECK);
+ removeMessages(CHECK_SEQUENCE_STEP);
+ mBroadcastsEnabled = false;
+ Slog.i(WWS_TAG, "WifiWatchdogService disabled");
+ }
+ break;
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.print("WatchdogStatus: ");
+ pw.print("State " + mStatus.state);
+ pw.println(", network [" + mStatus.ssid + ", " + mStatus.bssid + "]");
+ pw.print("checkCount " + mStatus.numFullDNSchecks);
+ pw.println(", bssids: " + mStatus.allBssids);
+ pw.print(", hasCheckMessages? " +
+ mHandler.hasMessages(WifiWatchdogHandler.CHECK_SEQUENCE_STEP));
+ pw.println(" hasSingleCheckMessages? " +
+ mHandler.hasMessages(WifiWatchdogHandler.SINGLE_DNS_CHECK));
+ pw.println("DNS check log str: " + mDNSCheckLogStr);
+ pw.println("lastSingleCheck: " + mStatus.lastSingleCheckTime);
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
+ */
+ private Boolean isWalledGardenTestEnabled() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
+ */
+ private String getWalledGardenUrl() {
+ String url = Settings.Secure.getString(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
+ if (TextUtils.isEmpty(url))
+ return "http://www.google.com/";
+ return url;
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
+ */
+ private String getWalledGardenPattern() {
+ String pattern = Settings.Secure.getString(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
+ if (TextUtils.isEmpty(pattern))
+ return "<title>.*Google.*</title>";
+ return pattern;
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
+ */
+ private boolean isWatchdogEnabled() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
+ }
+}