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;
2607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriffimport android.net.LinkProperties;
27654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.NetworkInfo;
28f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zhengimport android.net.wifi.RssiPacketCountInfo;
29654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.Message;
30654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.SystemClock;
31654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.provider.Settings;
32d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.provider.Settings.Secure;
339b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwaltimport android.util.Log;
34b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zhengimport android.util.LruCache;
35654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
366b66e9e4c95b1c866ea63a0122fc199994fd7053Irfan Sheriffimport com.android.internal.R;
3707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriffimport com.android.internal.util.AsyncChannel;
38654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.Protocol;
39654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.State;
40654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.StateMachine;
41654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
42654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.PrintWriter;
43b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zhengimport java.text.DecimalFormat;
44654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
45654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy/**
46b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * WifiWatchdogStateMachine monitors the connection to a WiFi network. When WiFi
47b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * connects at L2 layer, the beacons from access point reach the device and it
48b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * can maintain a connection, but the application connectivity can be flaky (due
49b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * to bigger packet size exchange).
50b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * <p>
51b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * We now monitor the quality of the last hop on WiFi using packet loss ratio as
52b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * an indicator to decide if the link is good enough to switch to Wi-Fi as the
53b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * uplink.
54b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * <p>
55b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * When WiFi is connected, the WiFi watchdog keeps sampling the RSSI and the
56b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * instant packet loss, and record it as per-AP loss-to-rssi statistics. When
57b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * the instant packet loss is higher than a threshold, the WiFi watchdog sends a
58b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * poor link notification to avoid WiFi connection temporarily.
59b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * <p>
60b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * While WiFi is being avoided, the WiFi watchdog keep watching the RSSI to
61b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * bring the WiFi connection back. Once the RSSI is high enough to achieve a
62b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * lower packet loss, a good link detection is sent such that the WiFi
63b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * connection become available again.
64b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * <p>
65b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * BSSID roaming has been taken into account. When user is moving across
66b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * multiple APs, the WiFi watchdog will detect that and keep watching the
67b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * currently connected AP.
68b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * <p>
69b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * Power impact should be minimal since much of the measurement relies on
70b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * passive statistics already being tracked at the driver and the polling is
71b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng * done when screen is turned on and the RSSI is in a certain range.
72654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy *
73654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @hide
74654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */
75654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levypublic class WifiWatchdogStateMachine extends StateMachine {
76654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
7707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff    /* STOPSHIP: Keep this configurable for debugging until ship */
7807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff    private static boolean DBG = false;
797f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff    private static final String TAG = "WifiWatchdogStateMachine";
80654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
81b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
82654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
83b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* Internal events */
84b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_WATCHDOG_TOGGLED                 = BASE + 1;
85b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_NETWORK_STATE_CHANGE             = BASE + 2;
86b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_RSSI_CHANGE                      = BASE + 3;
87b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_SUPPLICANT_STATE_CHANGE          = BASE + 4;
88b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_WIFI_RADIO_STATE_CHANGE          = BASE + 5;
89b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_WATCHDOG_SETTINGS_CHANGE         = BASE + 6;
90b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_BSSID_CHANGE                     = BASE + 7;
91b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_SCREEN_ON                        = BASE + 8;
92b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int EVENT_SCREEN_OFF                       = BASE + 9;
93654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
94b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* Internal messages */
95da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final int CMD_RSSI_FETCH                         = BASE + 11;
96654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
97b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* Notifications from/to WifiStateMachine */
98b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    static final int POOR_LINK_DETECTED                             = BASE + 21;
99b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    static final int GOOD_LINK_DETECTED                             = BASE + 22;
100b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
10117cf1f2bbc3f7d4f367dbbee935d2939957c0ef6Irfan Sheriff    public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
10217cf1f2bbc3f7d4f367dbbee935d2939957c0ef6Irfan Sheriff
103b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /*
104b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * RSSI levels as used by notification icon
105b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Level 4  -55 <= RSSI
106b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Level 3  -66 <= RSSI < -55
107b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Level 2  -77 <= RSSI < -67
108b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Level 1  -88 <= RSSI < -78
109b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Level 0         RSSI < -88
110b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
111d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
112b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
113b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi link statistics is monitored and recorded actively below this threshold.
114b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
115b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Larger threshold is more adaptive but increases sampling cost.
116b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
117f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng    private static final int LINK_MONITOR_LEVEL_THRESHOLD = WifiManager.RSSI_LEVELS - 1;
118d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
119b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
120b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Remember packet loss statistics of how many BSSIDs.
121b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
122b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Larger size is usually better but requires more space.
123b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
124b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int BSSID_STAT_CACHE_SIZE = 20;
125a81ac7c450d9d534c46abc7000cc53779a72c283Irfan Sheriff
126b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
127b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * RSSI range of a BSSID statistics.
128b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Within the range, (RSSI -> packet loss %) mappings are stored.
129b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
130b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Larger range is usually better but requires more space.
131a81ac7c450d9d534c46abc7000cc53779a72c283Irfan Sheriff     */
132b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int BSSID_STAT_RANGE_LOW_DBM  = -105;
133a81ac7c450d9d534c46abc7000cc53779a72c283Irfan Sheriff
134b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
135b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * See {@link #BSSID_STAT_RANGE_LOW_DBM}.
136b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
137b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int BSSID_STAT_RANGE_HIGH_DBM = -45;
138654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
139654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
140b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * How many consecutive empty data point to trigger a empty-cache detection.
141b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * In this case, a preset/default loss value (function on RSSI) is used.
142b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
143b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * In normal uses, some RSSI values may never be seen due to channel randomness.
144b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * However, the size of such empty RSSI chunk in normal use is generally 1~2.
145654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
146b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int BSSID_STAT_EMPTY_COUNT = 3;
147654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
148654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
149b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Sample interval for packet loss statistics, in msec.
150b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
151b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Smaller interval is more accurate but increases sampling cost (battery consumption).
152654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
153b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final long LINK_SAMPLING_INTERVAL_MS = 1 * 1000;
154654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
155b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
156b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Coefficients (alpha) for moving average for packet loss tracking.
157b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Must be within (0.0, 1.0).
158b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
159b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Equivalent number of samples: N = 2 / alpha - 1 .
160b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * We want the historic loss to base on more data points to be statistically reliable.
161b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * We want the current instant loss to base on less data points to be responsive.
162b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
163b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final double EXP_COEFFICIENT_RECORD  = 0.1;
16407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
165b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
166b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * See {@link #EXP_COEFFICIENT_RECORD}.
167b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
168b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final double EXP_COEFFICIENT_MONITOR = 0.5;
169b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
170b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
171b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Thresholds for sending good/poor link notifications, in packet loss %.
172b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Good threshold must be smaller than poor threshold.
173b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Use smaller poor threshold to avoid WiFi more aggressively.
174b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Use smaller good threshold to bring back WiFi more conservatively.
175b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
176b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * When approaching the boundary, loss ratio jumps significantly within a few dBs.
177b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * 50% loss threshold is a good balance between accuracy and reponsiveness.
178b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <=10% good threshold is a safe value to avoid jumping back to WiFi too easily.
179b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
180b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final double POOR_LINK_LOSS_THRESHOLD = 0.5;
181b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
182b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
183b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * See {@link #POOR_LINK_LOSS_THRESHOLD}.
184b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
185b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final double GOOD_LINK_LOSS_THRESHOLD = 0.1;
186b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
187b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
188b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Number of samples to confirm before sending a poor link notification.
189b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Response time = confirm_count * sample_interval .
190b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
191b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * A smaller threshold improves response speed but may suffer from randomness.
192b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * According to experiments, 3~5 are good values to achieve a balance.
193b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * These parameters should be tuned along with {@link #LINK_SAMPLING_INTERVAL_MS}.
194b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
195b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int POOR_LINK_SAMPLE_COUNT = 3;
196b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
197b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
198b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Minimum volume (converted from pkt/sec) to detect a poor link, to avoid randomness.
199b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
200b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * According to experiments, 1pkt/sec is too sensitive but 3pkt/sec is slightly unresponsive.
201b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
202b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final double POOR_LINK_MIN_VOLUME = 2.0 * LINK_SAMPLING_INTERVAL_MS / 1000.0;
203b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
204b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
205b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * When a poor link is detected, we scan over this range (based on current
206b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * poor link RSSI) for a target RSSI that satisfies a target packet loss.
207b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Refer to {@link #GOOD_LINK_TARGET}.
208b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
209b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * We want range_min not too small to avoid jumping back to WiFi too easily.
210b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
211b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int GOOD_LINK_RSSI_RANGE_MIN = 3;
212b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
213b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
214b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * See {@link #GOOD_LINK_RSSI_RANGE_MIN}.
215b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
216b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final int GOOD_LINK_RSSI_RANGE_MAX = 20;
217b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
218b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
219b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Adaptive good link target to avoid flapping.
220b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * When a poor link is detected, a good link target is calculated as follows:
221b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
222f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng     *      targetRSSI = min { rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
223f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng     *                   where rssi is within the above GOOD_LINK_RSSI_RANGE.
224b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     *      targetCount = sample_count[i] .
225b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
226b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * While WiFi is being avoided, we keep monitoring its signal strength.
227b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Good link notification is sent when we see current RSSI >= targetRSSI
228b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * for targetCount consecutive times.
229b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
230b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Index i is incremented each time after a poor link detection.
231b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Index i is decreased to at most k if the last poor link was at lease reduce_time[k] ago.
232b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
233b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Intuitively, larger index i makes it more difficult to get back to WiFi, avoiding flapping.
234b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * In experiments, (+9 dB / 30 counts) makes it quite difficult to achieve.
235f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng     * Avoid using it unless flapping is really bad (say, last poor link is < 1 min ago).
236b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
237b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final GoodLinkTarget[] GOOD_LINK_TARGET = {
238b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /*                  rssi_adj,       sample_count,   reduce_time */
239b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new GoodLinkTarget( 0,              3,              30 * 60000   ),
240b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new GoodLinkTarget( 3,              5,              5  * 60000   ),
241b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new GoodLinkTarget( 6,              10,             1  * 60000   ),
242b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new GoodLinkTarget( 9,              30,             0  * 60000   ),
243b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    };
244b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
245b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
246b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * The max time to avoid a BSSID, to prevent avoiding forever.
247b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * If current RSSI is at least min_rssi[i], the max avoidance time is at most max_time[i]
248b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
249b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * It is unusual to experience high packet loss at high RSSI. Something unusual must be
250b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * happening (e.g. strong interference). For higher signal strengths, we set the avoidance
251b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * time to be low to allow for quick turn around from temporary interference.
252b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * <p>
253b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * See {@link BssidStatistics#poorLinkDetected}.
254b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
255b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static final MaxAvoidTime[] MAX_AVOID_TIME = {
256b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /*                  max_time,           min_rssi */
257b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new MaxAvoidTime(   30 * 60000,         -200      ),
258b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new MaxAvoidTime(   5  * 60000,         -70       ),
259b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        new MaxAvoidTime(   0  * 60000,         -55       ),
260b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    };
261b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
262b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* Framework related */
263654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private Context mContext;
264654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private ContentResolver mContentResolver;
265654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WifiManager mWifiManager;
266654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private IntentFilter mIntentFilter;
267654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private BroadcastReceiver mBroadcastReceiver;
268b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private AsyncChannel mWsmChannel = new AsyncChannel();
269b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private WifiInfo mWifiInfo;
270b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private LinkProperties mLinkProperties;
271b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
272b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* System settingss related */
273b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static boolean sWifiOnly = false;
274b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private boolean mPoorNetworkDetectionEnabled;
275b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
276b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* Poor link detection related */
277b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private LruCache<String, BssidStatistics> mBssidCache =
278b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            new LruCache<String, BssidStatistics>(BSSID_STAT_CACHE_SIZE);
279b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private int mRssiFetchToken = 0;
280b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private int mCurrentSignalLevel;
281b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private BssidStatistics mCurrentBssid;
282b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private VolumeWeightedEMA mCurrentLoss;
283b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private boolean mIsScreenOn = true;
284b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static double sPresetLoss[];
285b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
286b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /* WiFi watchdog state machine related */
287654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private DefaultState mDefaultState = new DefaultState();
288654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
289654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
290654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private NotConnectedState mNotConnectedState = new NotConnectedState();
29107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff    private VerifyingLinkState mVerifyingLinkState = new VerifyingLinkState();
292654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private ConnectedState mConnectedState = new ConnectedState();
293654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
294b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private LinkMonitoringState mLinkMonitoringState = new LinkMonitoringState();
29519380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff    private OnlineState mOnlineState = new OnlineState();
296654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
297654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
298654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * STATE MAP
299654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *          Default
300654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     *         /       \
30107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff     * Disabled      Enabled
30207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff     *             /     \     \
30307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff     * NotConnected  Verifying  Connected
30407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff     *                         /---------\
30507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff     *                       (all other states)
306654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
307654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private WifiWatchdogStateMachine(Context context) {
3087f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff        super(TAG);
309654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContext = context;
310654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContentResolver = context.getContentResolver();
311654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
31207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        mWsmChannel.connectSync(mContext, getHandler(),
31307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                mWifiManager.getWifiStateMachineMessenger());
314654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
315654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        setupNetworkReceiver();
316654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
317b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        // the content observer to listen needs a handler
318654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        registerForSettingsChanges();
319d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy        registerForWatchdogToggle();
320654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        addState(mDefaultState);
321654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            addState(mWatchdogDisabledState, mDefaultState);
322654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            addState(mWatchdogEnabledState, mDefaultState);
323654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                addState(mNotConnectedState, mWatchdogEnabledState);
32407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                addState(mVerifyingLinkState, mWatchdogEnabledState);
325654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                addState(mConnectedState, mWatchdogEnabledState);
326654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    addState(mOnlineWatchState, mConnectedState);
327b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    addState(mLinkMonitoringState, mConnectedState);
32819380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff                    addState(mOnlineState, mConnectedState);
329654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
33007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        if (isWatchdogEnabled()) {
33107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            setInitialState(mNotConnectedState);
33207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        } else {
33307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            setInitialState(mWatchdogDisabledState);
33407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        }
335d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy        updateSettings();
336654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
337654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
338654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
3394ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy        ContentResolver contentResolver = context.getContentResolver();
3409b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwalt
3419b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwalt        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
3429b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwalt                Context.CONNECTIVITY_SERVICE);
3439b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwalt        sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
3449b2886e24301e5d4e7052ec4a6eaff273d3f516cRobert Greenwalt
345da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        // Watchdog is always enabled. Poor network detection can be seperately turned on/off
346ae094b27159864cb47015b96d18c5d32ef84fdc1Irfan Sheriff        // TODO: Remove this setting & clean up state machine since we always have
347ae094b27159864cb47015b96d18c5d32ef84fdc1Irfan Sheriff        // watchdog in an enabled state
348bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey        putSettingsGlobalBoolean(contentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
349ae094b27159864cb47015b96d18c5d32ef84fdc1Irfan Sheriff
350654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
351654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        wwsm.start();
352654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        return wwsm;
353654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
354654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
355654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private void setupNetworkReceiver() {
356654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mBroadcastReceiver = new BroadcastReceiver() {
357654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            @Override
358654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            public void onReceive(Context context, Intent intent) {
359654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                String action = intent.getAction();
360b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
36107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    obtainMessage(EVENT_RSSI_CHANGE,
36207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
363b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
364b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    sendMessage(EVENT_SUPPLICANT_STATE_CHANGE, intent);
365b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
366b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
367b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
368b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    sendMessage(EVENT_SCREEN_ON);
369b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
370b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    sendMessage(EVENT_SCREEN_OFF);
371654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
372b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,intent.getIntExtra(
373b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
374654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                }
375654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
376654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        };
377654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
378654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter = new IntentFilter();
379654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
380654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
381654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
382b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
383b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
384b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
38507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
386654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
387654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
388654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
389654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     * Observes the watchdog on/off setting, and takes action when changed.
390654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy     */
391d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    private void registerForWatchdogToggle() {
392654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
393654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            @Override
394654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            public void onChange(boolean selfChange) {
395654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                sendMessage(EVENT_WATCHDOG_TOGGLED);
396654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
397654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        };
398654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
399654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        mContext.getContentResolver().registerContentObserver(
400bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey                Settings.Global.getUriFor(Settings.Global.WIFI_WATCHDOG_ON),
401654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                false, contentObserver);
402654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
403654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
404654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    /**
405d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * Observes watchdogs secure setting changes.
406d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     */
407d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    private void registerForSettingsChanges() {
408d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy        ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
409d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy            @Override
410d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy            public void onChange(boolean selfChange) {
411d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy                sendMessage(EVENT_WATCHDOG_SETTINGS_CHANGE);
412d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy            }
413d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy        };
414d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
415d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy        mContext.getContentResolver().registerContentObserver(
416bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey                Settings.Global.getUriFor(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
417d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy                false, contentObserver);
418654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
419654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
420654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    public void dump(PrintWriter pw) {
421654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        pw.print("WatchdogStatus: ");
42207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        pw.print("State: " + getCurrentState());
42307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        pw.println("mWifiInfo: [" + mWifiInfo + "]");
42407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        pw.println("mLinkProperties: [" + mLinkProperties + "]");
42507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
42607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
427654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
428654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
429654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    private boolean isWatchdogEnabled() {
430bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey        boolean ret = getSettingsGlobalBoolean(
431bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey                mContentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
432b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG) logd("Watchdog enabled " + ret);
4334c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        return ret;
434654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
435654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
436d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    private void updateSettings() {
437b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG) logd("Updating secure settings");
438b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
439bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff        // disable poor network avoidance
440bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff        if (sWifiOnly) {
441bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff            logd("Disabling poor network avoidance for wi-fi only device");
442bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff            mPoorNetworkDetectionEnabled = false;
443bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff        } else {
444bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff            mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
44517cf1f2bbc3f7d4f367dbbee935d2939957c0ef6Irfan Sheriff                    Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
44617cf1f2bbc3f7d4f367dbbee935d2939957c0ef6Irfan Sheriff                    DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED);
447bbe461b78f9697775281d986bcf3c00904e1e62cIrfan Sheriff        }
4488dc6a1b2823f374a176fb21b8a174664a5f825faIsaac Levy    }
4498dc6a1b2823f374a176fb21b8a174664a5f825faIsaac Levy
450b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
451b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Default state, guard for unhandled messages.
452b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
453654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class DefaultState extends State {
454654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
4554c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public void enter() {
456b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
4574c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
4584c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff
4594c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        @Override
460654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
461d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy            switch (msg.what) {
462d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy                case EVENT_WATCHDOG_SETTINGS_CHANGE:
463d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy                    updateSettings();
464b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("Updating wifi-watchdog secure settings");
46507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
46607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                case EVENT_RSSI_CHANGE:
4674c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
46807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
46907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                case EVENT_WIFI_RADIO_STATE_CHANGE:
47007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                case EVENT_NETWORK_STATE_CHANGE:
471b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_SUPPLICANT_STATE_CHANGE:
472b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_BSSID_CHANGE:
4734c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                case CMD_RSSI_FETCH:
474f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
475f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
476b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    // ignore
477b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
478b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_SCREEN_ON:
479b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mIsScreenOn = true;
480b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
481b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_SCREEN_OFF:
482b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mIsScreenOn = false;
48307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
48407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                default:
485b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    loge("Unhandled message " + msg + " in state " + getCurrentState().getName());
48607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
487654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
488654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return HANDLED;
489654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
490654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
491654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
492b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
493b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi watchdog is disabled by the setting.
494b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
495654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class WatchdogDisabledState extends State {
496654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
4974c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public void enter() {
498b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
4994c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
5004c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff
5014c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        @Override
502654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
503654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
504654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WATCHDOG_TOGGLED:
505654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (isWatchdogEnabled())
506654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mNotConnectedState);
507654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
50807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                case EVENT_NETWORK_STATE_CHANGE:
50907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    Intent intent = (Intent) msg.obj;
51007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    NetworkInfo networkInfo = (NetworkInfo)
51107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
51207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
51307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    switch (networkInfo.getDetailedState()) {
51407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        case VERIFYING_POOR_LINK:
515b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            if (DBG) logd("Watchdog disabled, verify link");
516b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            sendLinkStatusNotification(true);
51707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            break;
51807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        default:
51907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            break;
52007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    }
52107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
522654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
523654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            return NOT_HANDLED;
524654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
525654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
526654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
527b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
528b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi watchdog is enabled by the setting.
529b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
530654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class WatchdogEnabledState extends State {
531654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
532654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
533b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
5344c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
535654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
536654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
537654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
538b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            Intent intent;
539654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
540654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WATCHDOG_TOGGLED:
541654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    if (!isWatchdogEnabled())
542654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mWatchdogDisabledState);
54307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
544b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
545654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_NETWORK_STATE_CHANGE:
546b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    intent = (Intent) msg.obj;
547b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    NetworkInfo networkInfo =
548b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
549b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("Network state change " + networkInfo.getDetailedState());
55007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
551b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mWifiInfo = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
552b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    updateCurrentBssid(mWifiInfo != null ? mWifiInfo.getBSSID() : null);
5534c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff
55407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    switch (networkInfo.getDetailedState()) {
55507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        case VERIFYING_POOR_LINK:
55607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            mLinkProperties = (LinkProperties) intent.getParcelableExtra(
55707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                                    WifiManager.EXTRA_LINK_PROPERTIES);
55819380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff                            if (mPoorNetworkDetectionEnabled) {
559edba852930bd2e9ab41f74f340595dafe500c756Irfan Sheriff                                if (mWifiInfo == null || mCurrentBssid == null) {
560edba852930bd2e9ab41f74f340595dafe500c756Irfan Sheriff                                    loge("Ignore, wifiinfo " + mWifiInfo +" bssid " + mCurrentBssid);
561b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    sendLinkStatusNotification(true);
56207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                                } else {
56307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                                    transitionTo(mVerifyingLinkState);
56407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                                }
56519380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff                            } else {
566b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                sendLinkStatusNotification(true);
56707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            }
56807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                            break;
56907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        case CONNECTED:
570da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                            transitionTo(mOnlineWatchState);
57132f04e9009046f72242932bf4e820802148e423aIrfan Sheriff                            break;
57232f04e9009046f72242932bf4e820802148e423aIrfan Sheriff                        default:
573654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                            transitionTo(mNotConnectedState);
57432f04e9009046f72242932bf4e820802148e423aIrfan Sheriff                            break;
575654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
57607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
577b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
578b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_SUPPLICANT_STATE_CHANGE:
579b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    intent = (Intent) msg.obj;
580b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    SupplicantState supplicantState = (SupplicantState) intent.getParcelableExtra(
581b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            WifiManager.EXTRA_NEW_STATE);
582b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (supplicantState == SupplicantState.COMPLETED) {
583b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        mWifiInfo = mWifiManager.getConnectionInfo();
584b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        updateCurrentBssid(mWifiInfo.getBSSID());
585b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    }
586b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
587b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
588654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_WIFI_RADIO_STATE_CHANGE:
589b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING)
590654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                        transitionTo(mNotConnectedState);
59107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
592b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
59307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                default:
59407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    return NOT_HANDLED;
595654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
596654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
59707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            return HANDLED;
598654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
599654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
600654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
601b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
602b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi is disconnected.
603b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
604654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class NotConnectedState extends State {
6054c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        @Override
6064c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public void enter() {
607b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
6084c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
609654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
610654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
611b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
612b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi is connected, but waiting for good link detection message.
613b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
61407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff    class VerifyingLinkState extends State {
615b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
616b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mSampleCount;
617b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
61807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        @Override
61907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        public void enter() {
620b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
621b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mSampleCount = 0;
622