blob: 274edae67c020955e6f1631c8b8912c15836007a [file] [log] [blame]
Isaac Levy654f5092011-07-13 17:41:45 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi;
18
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070019import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
Isaac Levy654f5092011-07-13 17:41:45 -070022import android.content.BroadcastReceiver;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070027import android.content.res.Resources;
Isaac Levy654f5092011-07-13 17:41:45 -070028import android.database.ContentObserver;
29import android.net.ConnectivityManager;
30import android.net.DnsPinger;
31import android.net.NetworkInfo;
32import android.net.Uri;
33import android.os.Message;
34import android.os.SystemClock;
Isaac Levy4ad39d62011-07-26 18:59:12 -070035import android.os.SystemProperties;
Isaac Levy654f5092011-07-13 17:41:45 -070036import android.provider.Settings;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070037import android.provider.Settings.Secure;
Isaac Levy654f5092011-07-13 17:41:45 -070038import android.util.Slog;
39
40import com.android.internal.util.Protocol;
41import com.android.internal.util.State;
42import com.android.internal.util.StateMachine;
43
Isaac Levy654f5092011-07-13 17:41:45 -070044import java.io.IOException;
Isaac Levy654f5092011-07-13 17:41:45 -070045import java.io.PrintWriter;
46import java.net.HttpURLConnection;
Isaac Levyd2fe04b2011-07-22 08:48:26 -070047import java.net.InetAddress;
Isaac Levy654f5092011-07-13 17:41:45 -070048import java.net.URL;
Isaac Levy79e43f62011-08-16 16:24:32 -070049import java.util.HashMap;
Isaac Levy654f5092011-07-13 17:41:45 -070050import java.util.HashSet;
51import java.util.List;
Isaac Levy654f5092011-07-13 17:41:45 -070052
53/**
54 * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
55 * network with multiple access points. After the framework successfully
56 * connects to an access point, the watchdog verifies connectivity by 'pinging'
57 * the configured DNS server using {@link DnsPinger}.
58 * <p>
59 * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
60 * that another AP might have internet access; otherwise the SSID is disabled.
61 * <p>
62 * On DNS success, the WatchdogService initiates a walled garden check via an
63 * http get. A browser window is activated if a walled garden is detected.
64 *
65 * @hide
66 */
67public class WifiWatchdogStateMachine extends StateMachine {
68
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070069
Isaac Levye0469752011-07-22 10:26:45 -070070 private static final boolean VDBG = false;
Isaac Levy654f5092011-07-13 17:41:45 -070071 private static final boolean DBG = true;
72 private static final String WWSM_TAG = "WifiWatchdogStateMachine";
Isaac Levy8dc6a1b2011-07-27 08:00:03 -070073 private static final String WATCHDOG_NOTIFICATION_ID = "Android.System.WifiWatchdog";
Isaac Levy654f5092011-07-13 17:41:45 -070074
75 private static final int WIFI_SIGNAL_LEVELS = 4;
76 /**
77 * Low signal is defined as less than or equal to cut off
78 */
79 private static final int LOW_SIGNAL_CUTOFF = 1;
80
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070081 private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
Isaac Levy79e43f62011-08-16 16:24:32 -070082 private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 30 * 60 * 1000;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070083 private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
Isaac Levy654f5092011-07-13 17:41:45 -070084
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070085 private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
Isaac Levy26a8d712011-08-09 15:35:59 -070086 private static final int DEFAULT_NUM_DNS_PINGS = 15; // Multiple pings to detect setup issues
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070087 private static final int DEFAULT_MIN_DNS_RESPONSES = 3;
Isaac Levy654f5092011-07-13 17:41:45 -070088
Isaac Levy88bae172011-07-27 15:41:36 -070089 private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -070090
91 private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
92
Isaac Levy88bae172011-07-27 15:41:36 -070093 // See http://go/clientsdns for usage approval
94 private static final String DEFAULT_WALLED_GARDEN_URL =
95 "http://clients3.google.com/generate_204";
96 private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
Isaac Levy26a8d712011-08-09 15:35:59 -070097 private static final int DNS_INTRATEST_PING_INTERVAL = 200; // Long delay to detect setup issues
Isaac Levy654f5092011-07-13 17:41:45 -070098
99 private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
100
101 /**
102 * Indicates the enable setting of WWS may have changed
103 */
104 private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1;
105
106 /**
107 * Indicates the wifi network state has changed. Passed w/ original intent
108 * which has a non-null networkInfo object
109 */
110 private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
111 /**
112 * Indicates the signal has changed. Passed with arg1
113 * {@link #mNetEventCounter} and arg2 [raw signal strength]
114 */
115 private static final int EVENT_RSSI_CHANGE = BASE + 3;
116 private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4;
117 private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700118 private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6;
Isaac Levy654f5092011-07-13 17:41:45 -0700119
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700120 private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 100;
121 private static final int MESSAGE_HANDLE_BAD_AP = BASE + 101;
Isaac Levy654f5092011-07-13 17:41:45 -0700122 /**
123 * arg1 == mOnlineWatchState.checkCount
124 */
125 private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 103;
126 private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 104;
127
128 private Context mContext;
129 private ContentResolver mContentResolver;
130 private WifiManager mWifiManager;
131 private DnsPinger mDnsPinger;
132 private IntentFilter mIntentFilter;
133 private BroadcastReceiver mBroadcastReceiver;
134
135 private DefaultState mDefaultState = new DefaultState();
136 private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
137 private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
138 private NotConnectedState mNotConnectedState = new NotConnectedState();
139 private ConnectedState mConnectedState = new ConnectedState();
140 private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
141 private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
142 private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
143 private WalledGardenState mWalledGardenState = new WalledGardenState();
144 private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
145
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700146 private long mDnsCheckShortIntervalMs;
147 private long mDnsCheckLongIntervalMs;
148 private long mWalledGardenIntervalMs;
149 private int mMaxSsidBlacklists;
150 private int mNumDnsPings;
151 private int mMinDnsResponses;
152 private int mDnsPingTimeoutMs;
153 private long mBlacklistFollowupIntervalMs;
154 private boolean mWalledGardenTestEnabled;
155 private String mWalledGardenUrl;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700156
157 private boolean mShowDisabledNotification;
Isaac Levy654f5092011-07-13 17:41:45 -0700158 /**
159 * The {@link WifiInfo} object passed to WWSM on network broadcasts
160 */
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700161 private WifiInfo mConnectionInfo;
Isaac Levy654f5092011-07-13 17:41:45 -0700162 private int mNetEventCounter = 0;
163
164 /**
165 * Currently maintained but not used, TODO
166 */
167 private HashSet<String> mBssids = new HashSet<String>();
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700168 private int mNumCheckFailures = 0;
Isaac Levy654f5092011-07-13 17:41:45 -0700169
170 private Long mLastWalledGardenCheckTime = null;
171
172 /**
173 * This is set by the blacklisted state and reset when connected to a new AP.
174 * It triggers a disableNetwork call if a DNS check fails.
175 */
176 public boolean mDisableAPNextFailure = false;
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700177 private ConnectivityManager mConnectivityManager;
178 private boolean mNotificationShown;
179 public boolean mHasConnectedWifiManager = false;
Isaac Levy654f5092011-07-13 17:41:45 -0700180
181 /**
182 * STATE MAP
183 * Default
184 * / \
185 * Disabled Enabled
186 * / \
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700187 * NotConnected Connected
Isaac Levy654f5092011-07-13 17:41:45 -0700188 * /---------\
189 * (all other states)
190 */
191 private WifiWatchdogStateMachine(Context context) {
192 super(WWSM_TAG);
193 mContext = context;
194 mContentResolver = context.getContentResolver();
195 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700196 mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
197 this.getHandler().getLooper(), this.getHandler(),
198 ConnectivityManager.TYPE_WIFI);
Isaac Levy654f5092011-07-13 17:41:45 -0700199
200 setupNetworkReceiver();
201
202 // The content observer to listen needs a handler
203 registerForSettingsChanges();
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700204 registerForWatchdogToggle();
Isaac Levy654f5092011-07-13 17:41:45 -0700205 addState(mDefaultState);
206 addState(mWatchdogDisabledState, mDefaultState);
207 addState(mWatchdogEnabledState, mDefaultState);
208 addState(mNotConnectedState, mWatchdogEnabledState);
209 addState(mConnectedState, mWatchdogEnabledState);
210 addState(mDnsCheckingState, mConnectedState);
211 addState(mDnsCheckFailureState, mConnectedState);
212 addState(mWalledGardenState, mConnectedState);
213 addState(mBlacklistedApState, mConnectedState);
214 addState(mOnlineWatchState, mConnectedState);
215
216 setInitialState(mWatchdogDisabledState);
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700217 updateSettings();
Isaac Levy654f5092011-07-13 17:41:45 -0700218 }
219
220 public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
Isaac Levy4ad39d62011-07-26 18:59:12 -0700221 ContentResolver contentResolver = context.getContentResolver();
222 // Disable for wifi only devices.
223 if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
224 "wifi-only".equals(SystemProperties.get("ro.carrier"))) {
225 putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
226 }
Isaac Levy654f5092011-07-13 17:41:45 -0700227 WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
228 wwsm.start();
229 wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
230 return wwsm;
231 }
232
233 /**
234 *
235 */
236 private void setupNetworkReceiver() {
237 mBroadcastReceiver = new BroadcastReceiver() {
238 @Override
239 public void onReceive(Context context, Intent intent) {
240 String action = intent.getAction();
241 if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
242 sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
243 } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
244 obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
245 intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
246 } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
247 sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
248 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
249 sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
250 intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
251 WifiManager.WIFI_STATE_UNKNOWN));
252 }
253 }
254 };
255
256 mIntentFilter = new IntentFilter();
257 mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
258 mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
259 mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
260 mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
261 }
262
263 /**
264 * Observes the watchdog on/off setting, and takes action when changed.
265 */
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700266 private void registerForWatchdogToggle() {
Isaac Levy654f5092011-07-13 17:41:45 -0700267 ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
268 @Override
269 public void onChange(boolean selfChange) {
270 sendMessage(EVENT_WATCHDOG_TOGGLED);
271 }
272 };
273
274 mContext.getContentResolver().registerContentObserver(
275 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
276 false, contentObserver);
277 }
278
279 /**
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700280 * Observes watchdogs secure setting changes.
281 */
282 private void registerForSettingsChanges() {
283 ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
284 @Override
285 public void onChange(boolean selfChange) {
286 sendMessage(EVENT_WATCHDOG_SETTINGS_CHANGE);
287 }
288 };
289
290 mContext.getContentResolver().registerContentObserver(
291 Settings.Secure.getUriFor(
292 Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS),
293 false, contentObserver);
294 mContext.getContentResolver().registerContentObserver(
295 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS),
296 false, contentObserver);
297 mContext.getContentResolver().registerContentObserver(
298 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
299 false, contentObserver);
300 mContext.getContentResolver().registerContentObserver(
301 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS),
302 false, contentObserver);
303 mContext.getContentResolver().registerContentObserver(
304 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS),
305 false, contentObserver);
306 mContext.getContentResolver().registerContentObserver(
307 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES),
308 false, contentObserver);
309 mContext.getContentResolver().registerContentObserver(
310 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS),
311 false, contentObserver);
312 mContext.getContentResolver().registerContentObserver(
313 Settings.Secure.getUriFor(
314 Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS),
315 false, contentObserver);
316 mContext.getContentResolver().registerContentObserver(
317 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
318 false, contentObserver);
319 mContext.getContentResolver().registerContentObserver(
320 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
321 false, contentObserver);
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700322 mContext.getContentResolver().registerContentObserver(
323 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP)
324 , false, contentObserver);
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700325 }
326
327 /**
Isaac Levy654f5092011-07-13 17:41:45 -0700328 * DNS based detection techniques do not work at all hotspots. The one sure
329 * way to check a walled garden is to see if a URL fetch on a known address
330 * fetches the data we expect
331 */
332 private boolean isWalledGardenConnection() {
Isaac Levy654f5092011-07-13 17:41:45 -0700333 HttpURLConnection urlConnection = null;
334 try {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700335 URL url = new URL(mWalledGardenUrl);
Isaac Levy654f5092011-07-13 17:41:45 -0700336 urlConnection = (HttpURLConnection) url.openConnection();
Isaac Levy88bae172011-07-27 15:41:36 -0700337 urlConnection.setInstanceFollowRedirects(false);
338 urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
339 urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
340 urlConnection.setUseCaches(false);
341 urlConnection.getInputStream();
342 // We got a valid response, but not from the real google
343 return urlConnection.getResponseCode() != 204;
Isaac Levy654f5092011-07-13 17:41:45 -0700344 } catch (IOException e) {
Isaac Levy88bae172011-07-27 15:41:36 -0700345 if (DBG) {
346 Slog.d(WWSM_TAG, "Walled garden check - probably not a portal: exception ", e);
347 }
Isaac Levy654f5092011-07-13 17:41:45 -0700348 return false;
349 } finally {
Isaac Levy88bae172011-07-27 15:41:36 -0700350 if (urlConnection != null) {
Isaac Levy654f5092011-07-13 17:41:45 -0700351 urlConnection.disconnect();
Isaac Levy88bae172011-07-27 15:41:36 -0700352 }
Isaac Levy654f5092011-07-13 17:41:45 -0700353 }
354 }
355
356 private boolean rssiStrengthAboveCutoff(int rssi) {
357 return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
358 }
359
360 public void dump(PrintWriter pw) {
361 pw.print("WatchdogStatus: ");
362 pw.print("State " + getCurrentState());
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700363 pw.println(", network [" + mConnectionInfo + "]");
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700364 pw.print("checkFailures " + mNumCheckFailures);
Isaac Levy654f5092011-07-13 17:41:45 -0700365 pw.println(", bssids: " + mBssids);
366 pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
367 }
368
Isaac Levy654f5092011-07-13 17:41:45 -0700369 private boolean isWatchdogEnabled() {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700370 return getSettingsBoolean(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true);
Isaac Levy654f5092011-07-13 17:41:45 -0700371 }
372
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700373 private void updateSettings() {
374 mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver,
375 Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS,
376 DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS);
377 mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver,
378 Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS,
379 DEFAULT_DNS_CHECK_LONG_INTERVAL_MS);
380 mMaxSsidBlacklists = Secure.getInt(mContentResolver,
381 Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS,
382 DEFAULT_MAX_SSID_BLACKLISTS);
383 mNumDnsPings = Secure.getInt(mContentResolver,
384 Secure.WIFI_WATCHDOG_NUM_DNS_PINGS,
385 DEFAULT_NUM_DNS_PINGS);
386 mMinDnsResponses = Secure.getInt(mContentResolver,
387 Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES,
388 DEFAULT_MIN_DNS_RESPONSES);
389 mDnsPingTimeoutMs = Secure.getInt(mContentResolver,
390 Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS,
391 DEFAULT_DNS_PING_TIMEOUT_MS);
392 mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver,
393 Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS,
394 DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS);
395 mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
396 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
397 mWalledGardenUrl = getSettingsStr(mContentResolver,
398 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL,
399 DEFAULT_WALLED_GARDEN_URL);
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700400 mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
401 Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
402 DEFAULT_WALLED_GARDEN_INTERVAL_MS);
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700403 mShowDisabledNotification = getSettingsBoolean(mContentResolver,
404 Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true);
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700405 }
Isaac Levy654f5092011-07-13 17:41:45 -0700406
407 /**
408 * Helper to return wait time left given a min interval and last run
409 *
410 * @param interval minimum wait interval
411 * @param lastTime last time action was performed in
412 * SystemClock.elapsedRealtime(). Null if never.
413 * @return non negative time to wait
414 */
415 private static long waitTime(long interval, Long lastTime) {
416 if (lastTime == null)
417 return 0;
418 long wait = interval + lastTime - SystemClock.elapsedRealtime();
419 return wait > 0 ? wait : 0;
420 }
421
422 private static String wifiInfoToStr(WifiInfo wifiInfo) {
423 if (wifiInfo == null)
424 return "null";
425 return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
426 }
427
428 /**
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700429 * Uses {@link #mConnectionInfo}.
430 */
431 private void updateBssids() {
432 String curSsid = mConnectionInfo.getSSID();
433 List<ScanResult> results = mWifiManager.getScanResults();
434 int oldNumBssids = mBssids.size();
435
436 if (results == null) {
437 if (DBG) {
438 Slog.d(WWSM_TAG, "updateBssids: Got null scan results!");
439 }
440 return;
441 }
442
443 for (ScanResult result : results) {
444 if (result == null || result.SSID == null) {
445 if (DBG) {
446 Slog.d(WWSM_TAG, "Received invalid scan result: " + result);
447 }
448 continue;
449 }
450 if (curSsid.equals(result.SSID))
451 mBssids.add(result.BSSID);
452 }
453 }
454
Isaac Levy654f5092011-07-13 17:41:45 -0700455 private void resetWatchdogState() {
Isaac Levy88bae172011-07-27 15:41:36 -0700456 if (VDBG) {
457 Slog.v(WWSM_TAG, "Resetting watchdog state...");
458 }
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700459 mConnectionInfo = null;
Isaac Levy654f5092011-07-13 17:41:45 -0700460 mDisableAPNextFailure = false;
461 mLastWalledGardenCheckTime = null;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700462 mNumCheckFailures = 0;
Isaac Levy654f5092011-07-13 17:41:45 -0700463 mBssids.clear();
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700464 cancelNetworkNotification();
Isaac Levy654f5092011-07-13 17:41:45 -0700465 }
466
467 private void popUpBrowser() {
468 Uri uri = Uri.parse("http://www.google.com");
469 Intent intent = new Intent(Intent.ACTION_VIEW, uri);
470 intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
471 Intent.FLAG_ACTIVITY_NEW_TASK);
472 mContext.startActivity(intent);
473 }
474
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700475 private void displayDisabledNetworkNotification(String ssid) {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700476 Resources r = Resources.getSystem();
477 CharSequence title =
478 r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled);
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700479 String msg = ssid +
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700480 r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled_detailed);
481
482 Notification wifiDisabledWarning = new Notification.Builder(mContext)
483 .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
484 .setDefaults(Notification.DEFAULT_ALL)
485 .setTicker(title)
486 .setContentTitle(title)
487 .setContentText(msg)
488 .setContentIntent(PendingIntent.getActivity(mContext, 0,
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700489 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700490 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
491 .setWhen(System.currentTimeMillis())
492 .setAutoCancel(true)
493 .getNotification();
494
495 NotificationManager notificationManager = (NotificationManager) mContext
496 .getSystemService(Context.NOTIFICATION_SERVICE);
497
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700498 notificationManager.notify(WATCHDOG_NOTIFICATION_ID, 1, wifiDisabledWarning);
499 mNotificationShown = true;
500 }
501
502 public void cancelNetworkNotification() {
503 if (mNotificationShown) {
504 NotificationManager notificationManager = (NotificationManager) mContext
505 .getSystemService(Context.NOTIFICATION_SERVICE);
506 notificationManager.cancel(WATCHDOG_NOTIFICATION_ID, 1);
507 mNotificationShown = false;
508 }
Isaac Levy654f5092011-07-13 17:41:45 -0700509 }
510
Isaac Levy4ad39d62011-07-26 18:59:12 -0700511 /**
512 * @return true if there is definitely no mobile data (we'll be less aggressive)
513 */
514 private boolean hasNoMobileData() {
515 if (mConnectivityManager == null) {
516 mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
517 Context.CONNECTIVITY_SERVICE);
518 }
519 NetworkInfo mobileNetInfo = mConnectivityManager.getNetworkInfo(
520 ConnectivityManager.TYPE_MOBILE);
521 if (mobileNetInfo == null || !mobileNetInfo.isAvailable()) {
522 return true;
523 }
524 return false;
525 }
526
Isaac Levy654f5092011-07-13 17:41:45 -0700527 class DefaultState extends State {
528 @Override
529 public boolean processMessage(Message msg) {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700530 switch (msg.what) {
531 case EVENT_WATCHDOG_SETTINGS_CHANGE:
532 updateSettings();
533 if (VDBG) {
534 Slog.d(WWSM_TAG, "Updating wifi-watchdog secure settings");
535 }
536 return HANDLED;
537 }
Isaac Levy654f5092011-07-13 17:41:45 -0700538 if (VDBG) {
539 Slog.v(WWSM_TAG, "Caught message " + msg.what + " in state " +
540 getCurrentState().getName());
541 }
542 return HANDLED;
543 }
544 }
545
546 class WatchdogDisabledState extends State {
547 @Override
548 public boolean processMessage(Message msg) {
549 switch (msg.what) {
550 case EVENT_WATCHDOG_TOGGLED:
551 if (isWatchdogEnabled())
552 transitionTo(mNotConnectedState);
553 return HANDLED;
554 }
555 return NOT_HANDLED;
556 }
557 }
558
559 class WatchdogEnabledState extends State {
560 @Override
561 public void enter() {
562 resetWatchdogState();
563 mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
564 Slog.i(WWSM_TAG, "WifiWatchdogService enabled");
565 }
566
567 @Override
568 public boolean processMessage(Message msg) {
569 switch (msg.what) {
570 case EVENT_WATCHDOG_TOGGLED:
571 if (!isWatchdogEnabled())
572 transitionTo(mWatchdogDisabledState);
573 return HANDLED;
574 case EVENT_NETWORK_STATE_CHANGE:
575 Intent stateChangeIntent = (Intent) msg.obj;
576 NetworkInfo networkInfo = (NetworkInfo)
577 stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
578
579 switch (networkInfo.getState()) {
580 case CONNECTED:
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700581 cancelNetworkNotification();
Isaac Levy88bae172011-07-27 15:41:36 -0700582 WifiInfo wifiInfo = (WifiInfo)
583 stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
Isaac Levy654f5092011-07-13 17:41:45 -0700584 if (wifiInfo == null) {
585 Slog.e(WWSM_TAG, "Connected --> WifiInfo object null!");
586 return HANDLED;
587 }
588
589 if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
590 Slog.e(WWSM_TAG, "Received wifiInfo object with null elts: "
591 + wifiInfoToStr(wifiInfo));
592 return HANDLED;
593 }
594
595 initConnection(wifiInfo);
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700596 mConnectionInfo = wifiInfo;
597 updateBssids();
Isaac Levy654f5092011-07-13 17:41:45 -0700598 transitionTo(mDnsCheckingState);
599 mNetEventCounter++;
600 return HANDLED;
601 case DISCONNECTED:
602 case DISCONNECTING:
603 mNetEventCounter++;
604 transitionTo(mNotConnectedState);
605 return HANDLED;
606 }
607 return HANDLED;
608 case EVENT_WIFI_RADIO_STATE_CHANGE:
609 if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
610 Slog.i(WWSM_TAG, "WifiStateDisabling -- Resetting WatchdogState");
611 resetWatchdogState();
612 mNetEventCounter++;
613 transitionTo(mNotConnectedState);
614 }
615 return HANDLED;
616 }
617
618 return NOT_HANDLED;
619 }
620
621 /**
622 * @param wifiInfo Info object with non-null ssid and bssid
623 */
624 private void initConnection(WifiInfo wifiInfo) {
625 if (VDBG) {
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700626 Slog.v(WWSM_TAG, "Connected:: old " + wifiInfoToStr(mConnectionInfo) +
Isaac Levy654f5092011-07-13 17:41:45 -0700627 " ==> new " + wifiInfoToStr(wifiInfo));
628 }
629
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700630 if (mConnectionInfo == null || !wifiInfo.getSSID().equals(mConnectionInfo.getSSID())) {
Isaac Levy654f5092011-07-13 17:41:45 -0700631 resetWatchdogState();
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700632 } else if (!wifiInfo.getBSSID().equals(mConnectionInfo.getBSSID())) {
Isaac Levy654f5092011-07-13 17:41:45 -0700633 mDisableAPNextFailure = false;
634 }
Isaac Levy654f5092011-07-13 17:41:45 -0700635 }
636
637 @Override
638 public void exit() {
639 mContext.unregisterReceiver(mBroadcastReceiver);
640 Slog.i(WWSM_TAG, "WifiWatchdogService disabled");
641 }
642 }
643
644 class NotConnectedState extends State {
645 }
646
647 class ConnectedState extends State {
648 @Override
649 public boolean processMessage(Message msg) {
650 switch (msg.what) {
651 case EVENT_SCAN_RESULTS_AVAILABLE:
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700652 updateBssids();
Isaac Levy654f5092011-07-13 17:41:45 -0700653 return HANDLED;
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700654 case EVENT_WATCHDOG_SETTINGS_CHANGE:
655 // Stop current checks, but let state update
656 transitionTo(mOnlineWatchState);
657 return NOT_HANDLED;
Isaac Levy654f5092011-07-13 17:41:45 -0700658 }
659 return NOT_HANDLED;
660 }
Isaac Levy654f5092011-07-13 17:41:45 -0700661 }
662
663 class DnsCheckingState extends State {
Isaac Levy79e43f62011-08-16 16:24:32 -0700664 List<InetAddress> mDnsList;
665 int[] dnsCheckSuccesses;
666 String dnsCheckLogStr;
667 String[] dnsResponseStrs;
668 /** Keeps track of active dns pings. Map is from pingID to index in mDnsList */
669 HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
Isaac Levy654f5092011-07-13 17:41:45 -0700670
671 @Override
672 public void enter() {
Isaac Levy79e43f62011-08-16 16:24:32 -0700673 mDnsList = mDnsPinger.getDnsList();
674 int numDnses = mDnsList.size();
675 dnsCheckSuccesses = new int[numDnses];
676 dnsResponseStrs = new String[numDnses];
677 for (int i = 0; i < numDnses; i++)
678 dnsResponseStrs[i] = "";
679
Isaac Levy654f5092011-07-13 17:41:45 -0700680 if (DBG) {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700681 dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
Isaac Levy79e43f62011-08-16 16:24:32 -0700682 mDnsList, mConnectionInfo.getSSID());
683 Slog.d(WWSM_TAG, dnsCheckLogStr);
Isaac Levy654f5092011-07-13 17:41:45 -0700684 }
685
Isaac Levy79e43f62011-08-16 16:24:32 -0700686 idDnsMap.clear();
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700687 for (int i=0; i < mNumDnsPings; i++) {
Isaac Levy79e43f62011-08-16 16:24:32 -0700688 for (int j = 0; j < numDnses; j++) {
689 idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
690 DNS_INTRATEST_PING_INTERVAL * i), j);
691 }
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700692 }
Isaac Levy654f5092011-07-13 17:41:45 -0700693 }
694
695 @Override
696 public boolean processMessage(Message msg) {
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700697 if (msg.what != DnsPinger.DNS_PING_RESULT) {
Isaac Levy654f5092011-07-13 17:41:45 -0700698 return NOT_HANDLED;
699 }
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700700
701 int pingID = msg.arg1;
702 int pingResponseTime = msg.arg2;
703
Isaac Levy79e43f62011-08-16 16:24:32 -0700704 Integer dnsServerId = idDnsMap.get(pingID);
705 if (dnsServerId == null) {
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700706 Slog.w(WWSM_TAG, "Received a Dns response with unknown ID!");
Isaac Levy654f5092011-07-13 17:41:45 -0700707 return HANDLED;
708 }
Isaac Levy79e43f62011-08-16 16:24:32 -0700709
710 idDnsMap.remove(pingID);
Isaac Levy654f5092011-07-13 17:41:45 -0700711 if (pingResponseTime >= 0)
Isaac Levy79e43f62011-08-16 16:24:32 -0700712 dnsCheckSuccesses[dnsServerId]++;
Isaac Levy654f5092011-07-13 17:41:45 -0700713
714 if (DBG) {
715 if (pingResponseTime >= 0) {
Isaac Levy79e43f62011-08-16 16:24:32 -0700716 dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
Isaac Levy654f5092011-07-13 17:41:45 -0700717 } else {
Isaac Levy79e43f62011-08-16 16:24:32 -0700718 dnsResponseStrs[dnsServerId] += "|x";
Isaac Levy654f5092011-07-13 17:41:45 -0700719 }
720 }
721
Isaac Levy654f5092011-07-13 17:41:45 -0700722 /**
723 * After a full ping count, if we have more responses than this
724 * cutoff, the outcome is success; else it is 'failure'.
725 */
Isaac Levy654f5092011-07-13 17:41:45 -0700726
727 /**
728 * Our final success count will be at least this big, so we're
729 * guaranteed to succeed.
730 */
Isaac Levy79e43f62011-08-16 16:24:32 -0700731 if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
Isaac Levy654f5092011-07-13 17:41:45 -0700732 // DNS CHECKS OK, NOW WALLED GARDEN
733 if (DBG) {
Isaac Levy79e43f62011-08-16 16:24:32 -0700734 Slog.d(WWSM_TAG, makeLogString() + " SUCCESS");
Isaac Levy654f5092011-07-13 17:41:45 -0700735 }
736
737 if (!shouldCheckWalledGarden()) {
738 transitionTo(mOnlineWatchState);
739 return HANDLED;
740 }
741
742 mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
743 if (isWalledGardenConnection()) {
744 if (DBG)
745 Slog.d(WWSM_TAG,
746 "Walled garden test complete - walled garden detected");
747 transitionTo(mWalledGardenState);
748 } else {
749 if (DBG)
750 Slog.d(WWSM_TAG, "Walled garden test complete - online");
751 transitionTo(mOnlineWatchState);
752 }
753 return HANDLED;
754 }
755
Isaac Levy79e43f62011-08-16 16:24:32 -0700756 if (idDnsMap.isEmpty()) {
Isaac Levy654f5092011-07-13 17:41:45 -0700757 if (DBG) {
Isaac Levy79e43f62011-08-16 16:24:32 -0700758 Slog.d(WWSM_TAG, makeLogString() + " FAILURE");
Isaac Levy654f5092011-07-13 17:41:45 -0700759 }
760 transitionTo(mDnsCheckFailureState);
761 return HANDLED;
762 }
763
Isaac Levy654f5092011-07-13 17:41:45 -0700764 return HANDLED;
765 }
766
Isaac Levy79e43f62011-08-16 16:24:32 -0700767 private String makeLogString() {
768 String logStr = dnsCheckLogStr;
769 for (String respStr : dnsResponseStrs)
770 logStr += " [" + respStr + "]";
771 return logStr;
772 }
773
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700774 @Override
775 public void exit() {
776 mDnsPinger.cancelPings();
777 }
778
Isaac Levy654f5092011-07-13 17:41:45 -0700779 private boolean shouldCheckWalledGarden() {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700780 if (!mWalledGardenTestEnabled) {
Isaac Levy654f5092011-07-13 17:41:45 -0700781 if (VDBG)
782 Slog.v(WWSM_TAG, "Skipping walled garden check - disabled");
783 return false;
784 }
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700785 long waitTime = waitTime(mWalledGardenIntervalMs,
Isaac Levy654f5092011-07-13 17:41:45 -0700786 mLastWalledGardenCheckTime);
787 if (waitTime > 0) {
788 if (DBG) {
789 Slog.d(WWSM_TAG, "Skipping walled garden check - wait " +
790 waitTime + " ms.");
791 }
792 return false;
793 }
794 return true;
795 }
Isaac Levy654f5092011-07-13 17:41:45 -0700796 }
797
798 class OnlineWatchState extends State {
799 /**
800 * Signals a short-wait message is enqueued for the current 'guard' counter
801 */
802 boolean unstableSignalChecks = false;
803
804 /**
805 * The signal is unstable. We should enqueue a short-wait check, if one is enqueued
806 * already
807 */
808 boolean signalUnstable = false;
809
810 /**
811 * A monotonic counter to ensure that at most one check message will be processed from any
812 * set of check messages currently enqueued. Avoids duplicate checks when a low-signal
813 * event is observed.
814 */
815 int checkGuard = 0;
816 Long lastCheckTime = null;
817
Isaac Levy79e43f62011-08-16 16:24:32 -0700818 /** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */
819 HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700820
Isaac Levy654f5092011-07-13 17:41:45 -0700821 @Override
822 public void enter() {
823 lastCheckTime = SystemClock.elapsedRealtime();
824 signalUnstable = false;
825 checkGuard++;
826 unstableSignalChecks = false;
Isaac Levy79e43f62011-08-16 16:24:32 -0700827 pingInfoMap.clear();
Isaac Levy654f5092011-07-13 17:41:45 -0700828 triggerSingleDnsCheck();
829 }
830
831 @Override
832 public boolean processMessage(Message msg) {
833 switch (msg.what) {
834 case EVENT_RSSI_CHANGE:
835 if (msg.arg1 != mNetEventCounter) {
836 if (DBG) {
837 Slog.d(WWSM_TAG, "Rssi change message out of sync, ignoring");
838 }
839 return HANDLED;
840 }
841 int newRssi = msg.arg2;
842 signalUnstable = !rssiStrengthAboveCutoff(newRssi);
843 if (VDBG) {
844 Slog.v(WWSM_TAG, "OnlineWatchState:: new rssi " + newRssi + " --> level " +
845 WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
846 }
847
848 if (signalUnstable && !unstableSignalChecks) {
849 if (VDBG) {
850 Slog.v(WWSM_TAG, "Sending triggered check msg");
851 }
852 triggerSingleDnsCheck();
853 }
854 return HANDLED;
855 case MESSAGE_SINGLE_DNS_CHECK:
856 if (msg.arg1 != checkGuard) {
857 if (VDBG) {
858 Slog.v(WWSM_TAG, "Single check msg out of sync, ignoring.");
859 }
860 return HANDLED;
861 }
862 lastCheckTime = SystemClock.elapsedRealtime();
Isaac Levy79e43f62011-08-16 16:24:32 -0700863 pingInfoMap.clear();
864 for (InetAddress curDns: mDnsPinger.getDnsList()) {
865 pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
866 curDns);
867 }
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700868 return HANDLED;
869 case DnsPinger.DNS_PING_RESULT:
Isaac Levy79e43f62011-08-16 16:24:32 -0700870 InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
871 if (curDnsServer == null) {
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700872 return HANDLED;
873 }
Isaac Levy79e43f62011-08-16 16:24:32 -0700874 pingInfoMap.remove(msg.arg1);
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700875 int responseTime = msg.arg2;
Isaac Levy654f5092011-07-13 17:41:45 -0700876 if (responseTime >= 0) {
877 if (VDBG) {
Isaac Levy79e43f62011-08-16 16:24:32 -0700878 Slog.v(WWSM_TAG, "Single DNS ping OK. Response time: "
879 + responseTime + " from DNS " + curDnsServer);
Isaac Levy654f5092011-07-13 17:41:45 -0700880 }
Isaac Levy79e43f62011-08-16 16:24:32 -0700881 pingInfoMap.clear();
Isaac Levy654f5092011-07-13 17:41:45 -0700882
883 checkGuard++;
884 unstableSignalChecks = false;
885 triggerSingleDnsCheck();
886 } else {
Isaac Levy79e43f62011-08-16 16:24:32 -0700887 if (pingInfoMap.isEmpty()) {
888 if (DBG) {
889 Slog.d(WWSM_TAG, "Single dns ping failure. All dns servers failed, "
890 + "starting full checks.");
891 }
892 transitionTo(mDnsCheckingState);
Isaac Levy654f5092011-07-13 17:41:45 -0700893 }
Isaac Levy654f5092011-07-13 17:41:45 -0700894 }
895 return HANDLED;
896 }
897 return NOT_HANDLED;
898 }
899
Isaac Levyd2fe04b2011-07-22 08:48:26 -0700900 @Override
901 public void exit() {
902 mDnsPinger.cancelPings();
903 }
904
Isaac Levy654f5092011-07-13 17:41:45 -0700905 /**
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700906 * Times a dns check with an interval based on {@link #signalUnstable}
Isaac Levy654f5092011-07-13 17:41:45 -0700907 */
908 private void triggerSingleDnsCheck() {
909 long waitInterval;
910 if (signalUnstable) {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700911 waitInterval = mDnsCheckShortIntervalMs;
Isaac Levy654f5092011-07-13 17:41:45 -0700912 unstableSignalChecks = true;
913 } else {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700914 waitInterval = mDnsCheckLongIntervalMs;
Isaac Levy654f5092011-07-13 17:41:45 -0700915 }
916 sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
917 waitTime(waitInterval, lastCheckTime));
918 }
919 }
920
921 class DnsCheckFailureState extends State {
Isaac Levy4ad39d62011-07-26 18:59:12 -0700922
Isaac Levy654f5092011-07-13 17:41:45 -0700923 @Override
924 public void enter() {
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700925 mNumCheckFailures++;
Isaac Levy654f5092011-07-13 17:41:45 -0700926 obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
927 }
928
929 @Override
930 public boolean processMessage(Message msg) {
931 if (msg.what != MESSAGE_HANDLE_BAD_AP) {
932 return NOT_HANDLED;
933 }
934
935 if (msg.arg1 != mNetEventCounter) {
936 if (VDBG) {
937 Slog.v(WWSM_TAG, "Msg out of sync, ignoring...");
938 }
939 return HANDLED;
940 }
941
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700942 if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
943 || mNumCheckFailures >= mMaxSsidBlacklists) {
Isaac Levy4ad39d62011-07-26 18:59:12 -0700944 if (hasNoMobileData()) {
945 Slog.w(WWSM_TAG, "Would disable bad network, but device has no mobile data!" +
946 " Going idle...");
947 // This state should be called idle -- will be changing flow.
948 transitionTo(mNotConnectedState);
949 return HANDLED;
950 }
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700951
Isaac Levy654f5092011-07-13 17:41:45 -0700952 // TODO : Unban networks if they had low signal ?
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700953 Slog.i(WWSM_TAG, "Disabling current SSID " + wifiInfoToStr(mConnectionInfo)
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700954 + ". " + "numCheckFailures " + mNumCheckFailures
955 + ", numAPs " + mBssids.size());
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700956 int networkId = mConnectionInfo.getNetworkId();
957 if (!mHasConnectedWifiManager) {
958 mWifiManager.asyncConnect(mContext, getHandler());
959 mHasConnectedWifiManager = true;
960 }
961 mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE);
962 if (mShowDisabledNotification && mConnectionInfo.isExplicitConnect()) {
963 displayDisabledNetworkNotification(mConnectionInfo.getSSID());
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700964 }
Isaac Levy654f5092011-07-13 17:41:45 -0700965 transitionTo(mNotConnectedState);
966 } else {
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700967 Slog.i(WWSM_TAG, "Blacklisting current BSSID. " + wifiInfoToStr(mConnectionInfo)
Isaac Levyd7b3e6a2011-07-20 18:15:30 -0700968 + "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size());
Isaac Levy654f5092011-07-13 17:41:45 -0700969
Isaac Levy8dc6a1b2011-07-27 08:00:03 -0700970 mWifiManager.addToBlacklist(mConnectionInfo.getBSSID());
Isaac Levy654f5092011-07-13 17:41:45 -0700971 mWifiManager.reassociate();
972 transitionTo(mBlacklistedApState);
973 }
974 return HANDLED;
975 }
976 }
977
978 class WalledGardenState extends State {
979 @Override
980 public void enter() {
981 obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
982 }
983
984 @Override
985 public boolean processMessage(Message msg) {
986 if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
987 return NOT_HANDLED;
988 }
989
990 if (msg.arg1 != mNetEventCounter) {
991 if (VDBG) {
992 Slog.v(WWSM_TAG, "WalledGardenState::Msg out of sync, ignoring...");
993 }
994 return HANDLED;
995 }
996 popUpBrowser();
997 transitionTo(mOnlineWatchState);
998 return HANDLED;
999 }
1000 }
1001
1002 class BlacklistedApState extends State {
1003 @Override
1004 public void enter() {
1005 mDisableAPNextFailure = true;
1006 sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
Isaac Levyd7b3e6a2011-07-20 18:15:30 -07001007 mBlacklistFollowupIntervalMs);
Isaac Levy654f5092011-07-13 17:41:45 -07001008 }
1009
1010 @Override
1011 public boolean processMessage(Message msg) {
1012 if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
1013 return NOT_HANDLED;
1014 }
1015
1016 if (msg.arg1 != mNetEventCounter) {
1017 if (VDBG) {
1018 Slog.v(WWSM_TAG, "BlacklistedApState::Msg out of sync, ignoring...");
1019 }
1020 return HANDLED;
1021 }
1022
1023 transitionTo(mDnsCheckingState);
1024 return HANDLED;
1025 }
1026 }
Isaac Levyd7b3e6a2011-07-20 18:15:30 -07001027
1028
1029 /**
1030 * Convenience function for retrieving a single secure settings value
1031 * as a string with a default value.
1032 *
1033 * @param cr The ContentResolver to access.
1034 * @param name The name of the setting to retrieve.
1035 * @param def Value to return if the setting is not defined.
1036 *
1037 * @return The setting's current value, or 'def' if it is not defined
1038 */
1039 private static String getSettingsStr(ContentResolver cr, String name, String def) {
1040 String v = Settings.Secure.getString(cr, name);
1041 return v != null ? v : def;
1042 }
1043
1044 /**
1045 * Convenience function for retrieving a single secure settings value
1046 * as a boolean. Note that internally setting values are always
1047 * stored as strings; this function converts the string to a boolean
1048 * for you. The default value will be returned if the setting is
1049 * not defined or not a valid boolean.
1050 *
1051 * @param cr The ContentResolver to access.
1052 * @param name The name of the setting to retrieve.
1053 * @param def Value to return if the setting is not defined.
1054 *
1055 * @return The setting's current value, or 'def' if it is not defined
1056 * or not a valid boolean.
1057 */
1058 private static boolean getSettingsBoolean(ContentResolver cr, String name, boolean def) {
1059 return Settings.Secure.getInt(cr, name, def ? 1 : 0) == 1;
1060 }
1061
1062 /**
1063 * Convenience function for updating a single settings value as an
1064 * integer. This will either create a new entry in the table if the
1065 * given name does not exist, or modify the value of the existing row
1066 * with that name. Note that internally setting values are always
1067 * stored as strings, so this function converts the given value to a
1068 * string before storing it.
1069 *
1070 * @param cr The ContentResolver to access.
1071 * @param name The name of the setting to modify.
1072 * @param value The new value for the setting.
1073 * @return true if the value was set, false on database errors
1074 */
1075 private static boolean putSettingsBoolean(ContentResolver cr, String name, boolean value) {
1076 return Settings.Secure.putInt(cr, name, value ? 1 : 0);
1077 }
1078
1079
Isaac Levy654f5092011-07-13 17:41:45 -07001080}