WifiWatchdogStateMachine.java revision 654f5090754e4e1bf4c1736d0a24769a15a6037e
1654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy/*
2654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Copyright (C) 2011 The Android Open Source Project
3654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *
4654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Licensed under the Apache License, Version 2.0 (the "License");
5654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * you may not use this file except in compliance with the License.
6654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * You may obtain a copy of the License at
7654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *
8654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *      http://www.apache.org/licenses/LICENSE-2.0
9654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *
10654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Unless required by applicable law or agreed to in writing, software
11654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * distributed under the License is distributed on an "AS IS" BASIS,
12654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * See the License for the specific language governing permissions and
14654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * limitations under the License.
15654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */
16654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
17654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levypackage android.net.wifi;
18654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
19654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.BroadcastReceiver;
20654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.ContentResolver;
21654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.Context;
22654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.Intent;
23654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.IntentFilter;
24654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.database.ContentObserver;
25654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.ConnectivityManager;
26654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.DnsPinger;
27654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.NetworkInfo;
28654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.Uri;
29654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.Message;
30654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.SystemClock;
31654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.provider.Settings;
32654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.text.TextUtils;
33654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.util.Slog;
34654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
35654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.Protocol;
36654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.State;
37654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.StateMachine;
38654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
39654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.BufferedInputStream;
40654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.IOException;
41654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.InputStream;
42654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.PrintWriter;
43654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.net.HttpURLConnection;
44654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.net.URL;
45654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.HashSet;
46654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.List;
47654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.Scanner;
48654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
49654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy/**
50654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
51654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * network with multiple access points. After the framework successfully
52654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * connects to an access point, the watchdog verifies connectivity by 'pinging'
53654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * the configured DNS server using {@link DnsPinger}.
54654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * <p>
55654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
56654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * that another AP might have internet access; otherwise the SSID is disabled.
57654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * <p>
58654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * On DNS success, the WatchdogService initiates a walled garden check via an
59654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * http get. A browser window is activated if a walled garden is detected.
60654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *
61654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @hide
62654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */
63654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levypublic class WifiWatchdogStateMachine extends StateMachine {
64654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
65654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final boolean VDBG = false;
66654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final boolean DBG = true;
67654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final String WWSM_TAG = "WifiWatchdogStateMachine";
68654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
69654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int WIFI_SIGNAL_LEVELS = 4;
70654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
71654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Low signal is defined as less than or equal to cut off
72654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
73654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int LOW_SIGNAL_CUTOFF = 1;
74654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
75654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL_MS = 2 * 60 * 1000;
76654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final long MIN_SINGLE_DNS_CHECK_INTERVAL_MS = 10 * 60 * 1000;
77654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final long MIN_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
78654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
79654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MAX_CHECKS_PER_SSID = 7;
80654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int NUM_DNS_PINGS = 5;
81654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final double MIN_DNS_RESPONSE_RATE = 0.50;
82654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
83654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int DNS_PING_TIMEOUT_MS = 800;
84654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final long DNS_PING_INTERVAL_MS = 100;
85654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
86654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final long BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
87654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
88654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
89654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
90654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
91654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Indicates the enable setting of WWS may have changed
92654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
93654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1;
94654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
95654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
96654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Indicates the wifi network state has changed. Passed w/ original intent
97654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * which has a non-null networkInfo object
98654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
99654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
100654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
101654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Indicates the signal has changed. Passed with arg1
102654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * {@link #mNetEventCounter} and arg2 [raw signal strength]
103654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
104654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int EVENT_RSSI_CHANGE = BASE + 3;
105654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4;
106654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
107654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
108654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MESSAGE_CHECK_STEP = BASE + 100;
109654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 101;
110654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MESSAGE_HANDLE_BAD_AP = BASE + 102;
111654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
112654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * arg1 == mOnlineWatchState.checkCount
113654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
114654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 103;
115654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 104;
116654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
117654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private Context mContext;
118654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private ContentResolver mContentResolver;
119654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WifiManager mWifiManager;
120654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private DnsPinger mDnsPinger;
121654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private IntentFilter mIntentFilter;
122654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private BroadcastReceiver mBroadcastReceiver;
123654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
124654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private DefaultState mDefaultState = new DefaultState();
125654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
126654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
127654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private NotConnectedState mNotConnectedState = new NotConnectedState();
128654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private ConnectedState mConnectedState = new ConnectedState();
129654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
130654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
131654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
132654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WalledGardenState mWalledGardenState = new WalledGardenState();
133654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
134654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
135654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
136654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * The {@link WifiInfo} object passed to WWSM on network broadcasts
137654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
138654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WifiInfo mInitialConnInfo;
139654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private int mNetEventCounter = 0;
140654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
141654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
142654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Currently maintained but not used, TODO
143654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
144654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private HashSet<String> mBssids = new HashSet<String>();
145654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private int mNumFullDNSchecks = 0;
146654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
147654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private Long mLastWalledGardenCheckTime = null;
148654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
149654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
150654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * This is set by the blacklisted state and reset when connected to a new AP.
151654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * It triggers a disableNetwork call if a DNS check fails.
152654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
153654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    public boolean mDisableAPNextFailure = false;
154654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
155654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
156654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * STATE MAP
157654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *          Default
158654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *         /       \
159654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Disabled     Enabled
160654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *             /       \
161654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Disconnected      Connected
162654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *                  /---------\
163654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *               (all other states)
164654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
165654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WifiWatchdogStateMachine(Context context) {
166654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        super(WWSM_TAG);
167654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContext = context;
168654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContentResolver = context.getContentResolver();
169654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
170654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context,
171654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                ConnectivityManager.TYPE_WIFI);
172654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
173654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        setupNetworkReceiver();
174654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
175654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        // The content observer to listen needs a handler
176654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        registerForSettingsChanges();
177654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        addState(mDefaultState);
178654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            addState(mWatchdogDisabledState, mDefaultState);
179654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            addState(mWatchdogEnabledState, mDefaultState);
180654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                addState(mNotConnectedState, mWatchdogEnabledState);
181654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                addState(mConnectedState, mWatchdogEnabledState);
182654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mDnsCheckingState, mConnectedState);
183654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mDnsCheckFailureState, mConnectedState);
184654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mWalledGardenState, mConnectedState);
185654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mBlacklistedApState, mConnectedState);
186654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mOnlineWatchState, mConnectedState);
187654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
188654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        setInitialState(mWatchdogDisabledState);
189654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
190654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
191654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
192654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
193654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        wwsm.start();
194654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
195654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return wwsm;
196654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
197654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
198654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
199654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy   *
200654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy   */
201654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void setupNetworkReceiver() {
202654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mBroadcastReceiver = new BroadcastReceiver() {
203654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            @Override
204654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            public void onReceive(Context context, Intent intent) {
205654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                String action = intent.getAction();
206654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
207654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
208654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
209654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
210654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
211654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
212654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
213654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
214654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
215654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
216654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                    WifiManager.WIFI_STATE_UNKNOWN));
217654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
218654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
219654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        };
220654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
221654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter = new IntentFilter();
222654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
223654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
224654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
225654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
226654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
227654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
228654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
229654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Observes the watchdog on/off setting, and takes action when changed.
230654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
231654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void registerForSettingsChanges() {
232654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
233654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            @Override
234654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            public void onChange(boolean selfChange) {
235654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                sendMessage(EVENT_WATCHDOG_TOGGLED);
236654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
237654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        };
238654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
239654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContext.getContentResolver().registerContentObserver(
240654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
241654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                false, contentObserver);
242654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
243654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
244654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
245654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * DNS based detection techniques do not work at all hotspots. The one sure
246654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * way to check a walled garden is to see if a URL fetch on a known address
247654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * fetches the data we expect
248654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
249654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private boolean isWalledGardenConnection() {
250654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        InputStream in = null;
251654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        HttpURLConnection urlConnection = null;
252654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        try {
253654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            URL url = new URL(getWalledGardenUrl());
254654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            urlConnection = (HttpURLConnection) url.openConnection();
255654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            in = new BufferedInputStream(urlConnection.getInputStream());
256654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            Scanner scanner = new Scanner(in);
257654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (scanner.findInLine(getWalledGardenPattern()) != null) {
258654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return false;
259654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            } else {
260654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return true;
261654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
262654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        } catch (IOException e) {
263654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return false;
264654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        } finally {
265654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (in != null) {
266654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                try {
267654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    in.close();
268654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } catch (IOException e) {
269654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
270654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
271654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (urlConnection != null)
272654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                urlConnection.disconnect();
273654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
274654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
275654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
276654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private boolean rssiStrengthAboveCutoff(int rssi) {
277654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
278654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
279654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
280654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    public void dump(PrintWriter pw) {
281654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.print("WatchdogStatus: ");
282654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.print("State " + getCurrentState());
283654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.println(", network [" + mInitialConnInfo + "]");
284654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.print("checkCount " + mNumFullDNSchecks);
285654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.println(", bssids: " + mBssids);
286654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
287654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
288654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
289654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
290654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
291654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
292654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private Boolean isWalledGardenTestEnabled() {
293654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return Settings.Secure.getInt(mContentResolver,
294654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
295654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
296654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
297654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
298654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
299654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
300654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private String getWalledGardenUrl() {
301654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        String url = Settings.Secure.getString(mContentResolver,
302654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
303654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        if (TextUtils.isEmpty(url))
304654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return "http://www.google.com/";
305654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return url;
306654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
307654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
308654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
309654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
310654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
311654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private String getWalledGardenPattern() {
312654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        String pattern = Settings.Secure.getString(mContentResolver,
313654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
314654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        if (TextUtils.isEmpty(pattern))
315654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return "<title>.*Google.*</title>";
316654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return pattern;
317654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
318654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
319654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
320654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
321654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
322654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private boolean isWatchdogEnabled() {
323654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return Settings.Secure.getInt(mContentResolver,
324654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
325654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
326654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
327654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
328654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
329654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Helper to return wait time left given a min interval and last run
330654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *
331654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @param interval minimum wait interval
332654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @param lastTime last time action was performed in
333654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *            SystemClock.elapsedRealtime(). Null if never.
334654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * @return non negative time to wait
335654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
336654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static long waitTime(long interval, Long lastTime) {
337654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        if (lastTime == null)
338654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return 0;
339654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        long wait = interval + lastTime - SystemClock.elapsedRealtime();
340654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return wait > 0 ? wait : 0;
341654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
342654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
343654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private static String wifiInfoToStr(WifiInfo wifiInfo) {
344654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        if (wifiInfo == null)
345654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return "null";
346654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
347654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
348654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
349654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
350654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy   *
351654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy   */
352654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void resetWatchdogState() {
353654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mInitialConnInfo = null;
354654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mDisableAPNextFailure = false;
355654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mLastWalledGardenCheckTime = null;
356654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mNumFullDNSchecks = 0;
357654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mBssids.clear();
358654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
359654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
360654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void popUpBrowser() {
361654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        Uri uri = Uri.parse("http://www.google.com");
362654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
363654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
364654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Intent.FLAG_ACTIVITY_NEW_TASK);
365654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContext.startActivity(intent);
366654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
367654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
368654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void sendCheckStepMessage(long delay) {
369654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay);
370654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
371654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
372654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class DefaultState extends State {
373654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
374654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
375654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (VDBG) {
376654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.v(WWSM_TAG, "Caught message " + msg.what + " in state " +
377654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        getCurrentState().getName());
378654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
379654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
380654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
381654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
382654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
383654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class WatchdogDisabledState extends State {
384654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
385654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
386654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
387654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WATCHDOG_TOGGLED:
388654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (isWatchdogEnabled())
389654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mNotConnectedState);
390654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
391654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
392654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return NOT_HANDLED;
393654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
394654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
395654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
396654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class WatchdogEnabledState extends State {
397654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
398654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
399654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            resetWatchdogState();
400654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
401654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            Slog.i(WWSM_TAG, "WifiWatchdogService enabled");
402654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
403654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
404654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
405654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
406654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
407654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WATCHDOG_TOGGLED:
408654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (!isWatchdogEnabled())
409654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mWatchdogDisabledState);
410654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
411654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_NETWORK_STATE_CHANGE:
412654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Intent stateChangeIntent = (Intent) msg.obj;
413654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    NetworkInfo networkInfo = (NetworkInfo)
414654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
415654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
416654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    switch (networkInfo.getState()) {
417654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        case CONNECTED:
418654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            // WifiInfo wifiInfo = (WifiInfo)
419654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            //         stateChangeIntent
420654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            //                 .getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
421654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            // TODO : Replace with above code when API is changed
422654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
423654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            if (wifiInfo == null) {
424654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                Slog.e(WWSM_TAG, "Connected --> WifiInfo object null!");
425654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                return HANDLED;
426654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            }
427654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
428654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
429654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                Slog.e(WWSM_TAG, "Received wifiInfo object with null elts: "
430654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                        + wifiInfoToStr(wifiInfo));
431654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                return HANDLED;
432654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            }
433654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
434654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            initConnection(wifiInfo);
435654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            transitionTo(mDnsCheckingState);
436654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            mNetEventCounter++;
437654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            return HANDLED;
438654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        case DISCONNECTED:
439654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        case DISCONNECTING:
440654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            mNetEventCounter++;
441654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            transitionTo(mNotConnectedState);
442654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            return HANDLED;
443654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
444654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
445654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WIFI_RADIO_STATE_CHANGE:
446654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
447654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        Slog.i(WWSM_TAG, "WifiStateDisabling -- Resetting WatchdogState");
448654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        resetWatchdogState();
449654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        mNetEventCounter++;
450654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mNotConnectedState);
451654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
452654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
453654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
454654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
455654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return NOT_HANDLED;
456654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
457654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
458654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        /**
459654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * @param wifiInfo Info object with non-null ssid and bssid
460654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         */
461654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        private void initConnection(WifiInfo wifiInfo) {
462654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (VDBG) {
463654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.v(WWSM_TAG, "Connected:: old " + wifiInfoToStr(mInitialConnInfo) +
464654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        " ==> new " + wifiInfoToStr(wifiInfo));
465654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
466654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
467654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (mInitialConnInfo == null || !wifiInfo.getSSID().equals(mInitialConnInfo.getSSID())) {
468654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                resetWatchdogState();
469654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            } else if (!wifiInfo.getBSSID().equals(mInitialConnInfo.getBSSID())) {
470654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                mDisableAPNextFailure = false;
471654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
472654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            mInitialConnInfo = wifiInfo;
473654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
474654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
475654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
476654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void exit() {
477654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            mContext.unregisterReceiver(mBroadcastReceiver);
478654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            Slog.i(WWSM_TAG, "WifiWatchdogService disabled");
479654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
480654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
481654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
482654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class NotConnectedState extends State {
483654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
484654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
485654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class ConnectedState extends State {
486654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
487654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
488654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
489654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_SCAN_RESULTS_AVAILABLE:
490654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    String curSsid = mInitialConnInfo.getSSID();
491654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    List<ScanResult> results = mWifiManager.getScanResults();
492654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    int oldNumBssids = mBssids.size();
493654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
494654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (results == null) {
495654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (DBG) {
496654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.d(WWSM_TAG, "updateBssids: Got null scan results!");
497654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
498654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        return HANDLED;
499654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
500654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
501654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    for (ScanResult result : results) {
502654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (result == null || result.SSID == null) {
503654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            if (VDBG) {
504654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                Slog.v(WWSM_TAG, "Received invalid scan result: " + result);
505654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            }
506654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            continue;
507654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
508654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (curSsid.equals(result.SSID))
509654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            mBssids.add(result.BSSID);
510654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
511654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
512654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
513654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return NOT_HANDLED;
514654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
515654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
516654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
517654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
518654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class DnsCheckingState extends State {
519654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        int dnsCheckTries = 0;
520654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        int dnsCheckSuccesses = 0;
521654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        String dnsCheckLogStr = "";
522654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
523654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
524654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
525654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            mNumFullDNSchecks++;
526654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            dnsCheckSuccesses = 0;
527654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            dnsCheckTries = 0;
528654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (DBG) {
529654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
530654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                dnsCheckLogStr = String.format("Dns Check %d.  Pinging %s on ssid [%s]: ",
531654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        mNumFullDNSchecks, mDnsPinger.getDns(), mInitialConnInfo.getSSID());
532654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
533654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
534654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            sendCheckStepMessage(0);
535654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
536654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
537654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
538654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
539654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.what != MESSAGE_CHECK_STEP) {
540654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return NOT_HANDLED;
541654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
542654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.arg1 != mNetEventCounter) {
543654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.d(WWSM_TAG, "Check step out of sync, ignoring...");
544654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
545654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
546654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
547654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
548654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    DNS_PING_TIMEOUT_MS);
549654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
550654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            dnsCheckTries++;
551654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (pingResponseTime >= 0)
552654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                dnsCheckSuccesses++;
553654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
554654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (DBG) {
555654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (pingResponseTime >= 0) {
556654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    dnsCheckLogStr += "|" + pingResponseTime;
557654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else {
558654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    dnsCheckLogStr += "|x";
559654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
560654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
561654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
562654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (VDBG) {
563654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.v(WWSM_TAG, dnsCheckLogStr);
564654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
565654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
566654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            /**
567654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * After a full ping count, if we have more responses than this
568654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * cutoff, the outcome is success; else it is 'failure'.
569654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             */
570654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            double pingResponseCutoff = MIN_DNS_RESPONSE_RATE * NUM_DNS_PINGS;
571654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            int remainingChecks = NUM_DNS_PINGS - dnsCheckTries;
572654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
573654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            /**
574654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * Our final success count will be at least this big, so we're
575654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * guaranteed to succeed.
576654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             */
577654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (dnsCheckSuccesses >= pingResponseCutoff) {
578654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                // DNS CHECKS OK, NOW WALLED GARDEN
579654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (DBG) {
580654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  SUCCESS");
581654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
582654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
583654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (!shouldCheckWalledGarden()) {
584654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    transitionTo(mOnlineWatchState);
585654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
586654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
587654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
588654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
589654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (isWalledGardenConnection()) {
590654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (DBG)
591654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        Slog.d(WWSM_TAG,
592654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                "Walled garden test complete - walled garden detected");
593654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    transitionTo(mWalledGardenState);
594654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else {
595654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (DBG)
596654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        Slog.d(WWSM_TAG, "Walled garden test complete - online");
597654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    transitionTo(mOnlineWatchState);
598654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
599654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
600654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
601654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
602654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            /**
603654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * Our final count will be at most the current count plus the
604654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             * remaining pings - we're guaranteed to fail.
605654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy             */
606654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (remainingChecks + dnsCheckSuccesses < pingResponseCutoff) {
607654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (DBG) {
608654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  FAILURE");
609654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
610654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                transitionTo(mDnsCheckFailureState);
611654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
612654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
613654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
614654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            // Still in dns check step
615654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            sendCheckStepMessage(DNS_PING_INTERVAL_MS);
616654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
617654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
618654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
619654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        private boolean shouldCheckWalledGarden() {
620654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (!isWalledGardenTestEnabled()) {
621654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (VDBG)
622654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.v(WWSM_TAG, "Skipping walled garden check - disabled");
623654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return false;
624654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
625654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL_MS,
626654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    mLastWalledGardenCheckTime);
627654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (waitTime > 0) {
628654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (DBG) {
629654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.d(WWSM_TAG, "Skipping walled garden check - wait " +
630654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            waitTime + " ms.");
631654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
632654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return false;
633654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
634654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return true;
635654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
636654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
637654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
638654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
639654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class OnlineWatchState extends State {
640654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        /**
641654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * Signals a short-wait message is enqueued for the current 'guard' counter
642654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         */
643654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        boolean unstableSignalChecks = false;
644654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
645654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        /**
646654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * The signal is unstable.  We should enqueue a short-wait check, if one is enqueued
647654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * already
648654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         */
649654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        boolean signalUnstable = false;
650654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
651654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        /**
652654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * A monotonic counter to ensure that at most one check message will be processed from any
653654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * set of check messages currently enqueued.  Avoids duplicate checks when a low-signal
654654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * event is observed.
655654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         */
656654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        int checkGuard = 0;
657654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        Long lastCheckTime = null;
658654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
659654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
660654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
661654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            lastCheckTime = SystemClock.elapsedRealtime();
662654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            signalUnstable = false;
663654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            checkGuard++;
664654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            unstableSignalChecks = false;
665654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            triggerSingleDnsCheck();
666654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
667654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
668654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
669654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
670654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
671654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_RSSI_CHANGE:
672654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (msg.arg1 != mNetEventCounter) {
673654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (DBG) {
674654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.d(WWSM_TAG, "Rssi change message out of sync, ignoring");
675654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
676654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        return HANDLED;
677654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
678654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    int newRssi = msg.arg2;
679654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    signalUnstable = !rssiStrengthAboveCutoff(newRssi);
680654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (VDBG) {
681654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        Slog.v(WWSM_TAG, "OnlineWatchState:: new rssi " + newRssi + " --> level " +
682654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
683654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
684654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
685654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (signalUnstable && !unstableSignalChecks) {
686654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (VDBG) {
687654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.v(WWSM_TAG, "Sending triggered check msg");
688654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
689654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        triggerSingleDnsCheck();
690654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
691654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
692654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case MESSAGE_SINGLE_DNS_CHECK:
693654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (msg.arg1 != checkGuard) {
694654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (VDBG) {
695654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.v(WWSM_TAG, "Single check msg out of sync, ignoring.");
696654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
697654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        return HANDLED;
698654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
699654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    lastCheckTime = SystemClock.elapsedRealtime();
700654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
701654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            DNS_PING_TIMEOUT_MS);
702654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (responseTime >= 0) {
703654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (VDBG) {
704654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: "
705654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                                    + responseTime);
706654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
707654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
708654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        checkGuard++;
709654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        unstableSignalChecks = false;
710654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        triggerSingleDnsCheck();
711654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    } else {
712654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        if (DBG) {
713654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks.");
714654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        }
715654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mDnsCheckingState);
716654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
717654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
718654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
719654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return NOT_HANDLED;
720654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
721654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
722654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        /**
723654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         * Times a dns check with an interval based on {@link #curSignalStable}
724654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy         */
725654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        private void triggerSingleDnsCheck() {
726654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            long waitInterval;
727654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (signalUnstable) {
728654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                waitInterval = MIN_LOW_SIGNAL_CHECK_INTERVAL_MS;
729654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                unstableSignalChecks = true;
730654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            } else {
731654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                waitInterval = MIN_SINGLE_DNS_CHECK_INTERVAL_MS;
732654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
733654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
734654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    waitTime(waitInterval, lastCheckTime));
735654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
736654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
737654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
738654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class DnsCheckFailureState extends State {
739654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
740654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
741654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
742654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
743654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
744654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
745654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
746654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.what != MESSAGE_HANDLE_BAD_AP) {
747654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return NOT_HANDLED;
748654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
749654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
750654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.arg1 != mNetEventCounter) {
751654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (VDBG) {
752654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.v(WWSM_TAG, "Msg out of sync, ignoring...");
753654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
754654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
755654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
756654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
757654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (mDisableAPNextFailure || mNumFullDNSchecks >= MAX_CHECKS_PER_SSID) {
758654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                // TODO : Unban networks if they had low signal ?
759654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.i(WWSM_TAG, "Disabling current SSID " + wifiInfoToStr(mInitialConnInfo)
760654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        + ".  " +
761654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size());
762654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                mWifiManager.disableNetwork(mInitialConnInfo.getNetworkId());
763654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                transitionTo(mNotConnectedState);
764654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            } else {
765654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                Slog.i(WWSM_TAG, "Blacklisting current BSSID.  " + wifiInfoToStr(mInitialConnInfo) +
766654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size());
767654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
768654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                mWifiManager.addToBlacklist(mInitialConnInfo.getBSSID());
769654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                mWifiManager.reassociate();
770654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                transitionTo(mBlacklistedApState);
771654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
772654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
773654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
774654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
775654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
776654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class WalledGardenState extends State {
777654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
778654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
779654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
780654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
781654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
782654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
783654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
784654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
785654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return NOT_HANDLED;
786654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
787654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
788654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.arg1 != mNetEventCounter) {
789654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (VDBG) {
790654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.v(WWSM_TAG, "WalledGardenState::Msg out of sync, ignoring...");
791654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
792654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
793654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
794654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            popUpBrowser();
795654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            transitionTo(mOnlineWatchState);
796654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
797654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
798654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
799654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
800654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class BlacklistedApState extends State {
801654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
802654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
803654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            mDisableAPNextFailure = true;
804654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
805654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    BLACKLIST_FOLLOWUP_INTERVAL_MS);
806654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
807654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
808654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
809654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
810654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
811654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return NOT_HANDLED;
812654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
813654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
814654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            if (msg.arg1 != mNetEventCounter) {
815654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                if (VDBG) {
816654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    Slog.v(WWSM_TAG, "BlacklistedApState::Msg out of sync, ignoring...");
817654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
818654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                return HANDLED;
819654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
820654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
821654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            transitionTo(mDnsCheckingState);
822654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
823654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
824654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
825654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy}
826