WifiWatchdogStateMachine.java revision 17cf1f2bbc3f7d4f367dbbee935d2939957c0ef6
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) {
55907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                                if (mWifiInfo == null) {
560b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    if (DBG) logd("Ignoring link verification, mWifiInfo is NULL");
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;
622b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentBssid.newLinkDetected();
623b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            sendMessage(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0));
62407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        }
62507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
626654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
627654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
628654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
629d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy                case EVENT_WATCHDOG_SETTINGS_CHANGE:
63019380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff                    updateSettings();
63107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    if (!mPoorNetworkDetectionEnabled) {
632b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        sendLinkStatusNotification(true);
63319380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff                    }
63407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
635b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
636b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_BSSID_CHANGE:
637b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    transitionTo(mVerifyingLinkState);
63807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
639b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
640b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case CMD_RSSI_FETCH:
641b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (msg.arg1 == mRssiFetchToken) {
642f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                        mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
643b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
644b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                LINK_SAMPLING_INTERVAL_MS);
645b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    }
646b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
647b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
648f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
649f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
650f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    int rssi = info.rssi;
651b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi);
652b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
653b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    long time = mCurrentBssid.mBssidAvoidTimeMax - SystemClock.elapsedRealtime();
654b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (time <= 0) {
655b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        // max avoidance time is met
656b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        if (DBG) logd("Max avoid time elapsed");
657b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        sendLinkStatusNotification(true);
658b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    } else {
659b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        if (rssi >= mCurrentBssid.mGoodLinkTargetRssi) {
660b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            if (++mSampleCount >= mCurrentBssid.mGoodLinkTargetCount) {
661b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                // link is good again
662b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                if (DBG) logd("Good link detected, rssi=" + rssi);
663b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                mCurrentBssid.mBssidAvoidTimeMax = 0;
664b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                sendLinkStatusNotification(true);
665b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            }
66607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        } else {
667b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            mSampleCount = 0;
668b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            if (DBG) logd("Link is still poor, time left=" + time);
66907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        }
67007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    }
67107573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
672b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
673f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
674b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("RSSI_FETCH_FAILED");
675b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
676b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
67707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                default:
67807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    return NOT_HANDLED;
679654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
68007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            return HANDLED;
681654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
682654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
683654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
684b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
685b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * WiFi is connected and link is verified.
686b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
68707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff    class ConnectedState extends State {
688654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
689654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
690b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
691654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
692b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
693654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
694654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
69507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            switch (msg.what) {
69607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                case EVENT_WATCHDOG_SETTINGS_CHANGE:
69707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    updateSettings();
698b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    // STOPSHIP: Remove this at ship
699b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    logd("Updated secure settings and turned debug on");
70007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    DBG = true;
701654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
70207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    if (mPoorNetworkDetectionEnabled) {
70307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        transitionTo(mOnlineWatchState);
70407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    } else {
70507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                        transitionTo(mOnlineState);
70607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    }
707654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    return HANDLED;
708654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
70907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            return NOT_HANDLED;
710654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
711654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    }
712654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
713b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
714b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * RSSI is high enough and don't need link monitoring.
715b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
716654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy    class OnlineWatchState extends State {
717b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        @Override
718654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public void enter() {
719b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
72007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            if (mPoorNetworkDetectionEnabled) {
721b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                // treat entry as an rssi change
72207573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                handleRssiChange();
72307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            } else {
72407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                transitionTo(mOnlineState);
72507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            }
72607573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        }
72707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
72807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff        private void handleRssiChange() {
729b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) {
730b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                transitionTo(mLinkMonitoringState);
7314c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff            } else {
732b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                // stay here
73307573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            }
734654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
735654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
736654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        @Override
737654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        public boolean processMessage(Message msg) {
738654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            switch (msg.what) {
739654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                case EVENT_RSSI_CHANGE:
7404c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
741b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    handleRssiChange();
7424c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    break;
7434c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                default:
7444c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    return NOT_HANDLED;
7454c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff            }
7464c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff            return HANDLED;
7474c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
7484c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff    }
74907573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
750b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
751b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Keep sampling the link and monitor any poor link situation.
752b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
753b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    class LinkMonitoringState extends State {
754b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
755b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mSampleCount;
756b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
757b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mLastRssi;
758b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mLastTxGood;
759b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mLastTxBad;
760b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
761b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        @Override
7624c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public void enter() {
763b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
764b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mSampleCount = 0;
765b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentLoss = new VolumeWeightedEMA(EXP_COEFFICIENT_MONITOR);
7664c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff            sendMessage(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0));
7674c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
76807573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff
769b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        @Override
7704c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public boolean processMessage(Message msg) {
7714c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff            switch (msg.what) {
7724c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                case EVENT_RSSI_CHANGE:
7734c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
774b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) {
775b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        // stay here;
7764c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    } else {
777b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        // we don't need frequent RSSI monitoring any more
7784c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                        transitionTo(mOnlineWatchState);
7794c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    }
78007573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
781b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
782b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_BSSID_CHANGE:
783b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    transitionTo(mLinkMonitoringState);
784b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
785b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
7864c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                case CMD_RSSI_FETCH:
787b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (!mIsScreenOn) {
788b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        transitionTo(mOnlineState);
789b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    } else if (msg.arg1 == mRssiFetchToken) {
790f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                        mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
7914c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                        sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
792b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                LINK_SAMPLING_INTERVAL_MS);
7934c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    }
7944c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    break;
795654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
796f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
797f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
798f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    int rssi = info.rssi;
799b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    int mrssi = (mLastRssi + rssi) / 2;
800f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    int txbad = info.txbad;
801f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                    int txgood = info.txgood;
802b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi + " mrssi=" + mrssi + " txbad="
803b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            + txbad + " txgood=" + txgood);
804b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
805b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    // skip the first data point as we want incremental values
806b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    long now = SystemClock.elapsedRealtime();
807b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (now - mCurrentBssid.mLastTimeSample < LINK_SAMPLING_INTERVAL_MS * 2) {
808b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
809b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        // update packet loss statistics
810b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        int dbad = txbad - mLastTxBad;
811b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        int dgood = txgood - mLastTxGood;
812b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        int dtotal = dbad + dgood;
813b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
814b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        if (dtotal > 0) {
815b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            // calculate packet loss in the last sampling interval
816b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            double loss = ((double) dbad) / ((double) dtotal);
817b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
818b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            mCurrentLoss.update(loss, dtotal);
819b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
820b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            if (DBG) {
821b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                DecimalFormat df = new DecimalFormat("#.##");
822b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                logd("Incremental loss=" + dbad + "/" + dtotal + " Current loss="
823b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                        + df.format(mCurrentLoss.mValue * 100) + "% volume="
824b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                        + df.format(mCurrentLoss.mVolume));
825b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            }
826b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
827b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            mCurrentBssid.updateLoss(mrssi, loss, dtotal);
828b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
829b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            // check for high packet loss and send poor link notification
830b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            if (mCurrentLoss.mValue > POOR_LINK_LOSS_THRESHOLD
831b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    && mCurrentLoss.mVolume > POOR_LINK_MIN_VOLUME) {
832b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                if (++mSampleCount >= POOR_LINK_SAMPLE_COUNT)
833b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    if (mCurrentBssid.poorLinkDetected(rssi)) {
834b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                        sendLinkStatusNotification(false);
835b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                        ++mRssiFetchToken;
836b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    }
837b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            } else {
838b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                mSampleCount = 0;
839b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            }
840b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        }
841654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy                    }
842b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
843b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mCurrentBssid.mLastTimeSample = now;
844b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mLastTxBad = txbad;
845b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mLastTxGood = txgood;
846b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mLastRssi = rssi;
84707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    break;
848b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
849f6307820c88e694e102824225b9d8caa6de75a30Yuhao Zheng                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
850b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    // can happen if we are waiting to get a disconnect notification
851b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (DBG) logd("RSSI_FETCH_FAILED");
8524c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff                    break;
853b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
85407573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                default:
85507573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff                    return NOT_HANDLED;
856654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy            }
85707573b32494acbabd21979d8b9584c1ed3f7a6adIrfan Sheriff            return HANDLED;
858654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy        }
8594c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff   }
860654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy
861b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
862b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Child state of ConnectedState indicating that we are online and there is nothing to do.
86319380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff     */
86419380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff    class OnlineState extends State {
8654c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        @Override
8664c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        public void enter() {
867b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd(getName());
868b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
869b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
870b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        @Override
871b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public boolean processMessage(Message msg) {
872b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            switch (msg.what) {
873b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                case EVENT_SCREEN_ON:
874b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    mIsScreenOn = true;
875b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (mPoorNetworkDetectionEnabled)
876b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        transitionTo(mOnlineWatchState);
877b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    break;
878b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                default:
879b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    return NOT_HANDLED;
880b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            }
881b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            return HANDLED;
8824c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        }
88319380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff    }
88419380daaf46815c80bd89fd9ca3af3c4095952b5Irfan Sheriff
885b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private void updateCurrentBssid(String bssid) {
886b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG) logd("Update current BSSID to " + (bssid != null ? bssid : "null"));
887b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
888b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        // if currently not connected, then set current BSSID to null
889b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (bssid == null) {
890b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (mCurrentBssid == null) return;
891b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentBssid = null;
892b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd("BSSID changed");
893b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            sendMessage(EVENT_BSSID_CHANGE);
894b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            return;
895b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
896b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
897b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        // if it is already the current BSSID, then done
898b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (mCurrentBssid != null && bssid.equals(mCurrentBssid.mBssid)) return;
899b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
900b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        // search for the new BSSID in the cache, add to cache if not found
901b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        mCurrentBssid = mBssidCache.get(bssid);
902b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (mCurrentBssid == null) {
903b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentBssid = new BssidStatistics(bssid);
904b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mBssidCache.put(bssid, mCurrentBssid);
905b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
906b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
907b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        // send BSSID change notification
908b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG) logd("BSSID changed");
909b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        sendMessage(EVENT_BSSID_CHANGE);
910b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    }
911b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
9124c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff    private int calculateSignalLevel(int rssi) {
913b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        int signalLevel = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
914b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG)
915b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            logd("RSSI current: " + mCurrentSignalLevel + " new: " + rssi + ", " + signalLevel);
9164c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff        return signalLevel;
9174c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff    }
9184c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff
919b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private void sendLinkStatusNotification(boolean isGood) {
920b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (DBG) logd("########################################");
921b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        if (isGood) {
922b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
923b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
924b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            logd("Good link notification is sent");
92590d57dfac3113247e2d38a2235254fc35d12856aIrfan Sheriff        } else {
926b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mWsmChannel.sendMessage(POOR_LINK_DETECTED);
927b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
928b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            logd("Poor link notification is sent");
92990d57dfac3113247e2d38a2235254fc35d12856aIrfan Sheriff        }
9304c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff    }
9314c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff
932d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    /**
933b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Convenience function for retrieving a single secure settings value as a
934b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * boolean. Note that internally setting values are always stored as
935b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * strings; this function converts the string to a boolean for you. The
936b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * default value will be returned if the setting is not defined or not a
937b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * valid boolean.
938d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     *
939d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param cr The ContentResolver to access.
940d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param name The name of the setting to retrieve.
941d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param def Value to return if the setting is not defined.
942b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * @return The setting's current value, or 'def' if it is not defined or not
943b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     *         a valid boolean.
944d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     */
945bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey    private static boolean getSettingsGlobalBoolean(ContentResolver cr, String name, boolean def) {
946bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey        return Settings.Global.getInt(cr, name, def ? 1 : 0) == 1;
947d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    }
948d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
949d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    /**
950b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Convenience function for updating a single settings value as an integer.
951b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * This will either create a new entry in the table if the given name does
952b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * not exist, or modify the value of the existing row with that name. Note
953b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * that internally setting values are always stored as strings, so this
954b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * function converts the given value to a string before storing it.
955d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     *
956d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param cr The ContentResolver to access.
957d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param name The name of the setting to modify.
958d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @param value The new value for the setting.
959d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     * @return true if the value was set, false on database errors
960d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy     */
961bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey    private static boolean putSettingsGlobalBoolean(ContentResolver cr, String name, boolean value) {
962bdfce2ec05a3e9ca6acd6711de6133e06f2446e6Jeff Sharkey        return Settings.Global.putInt(cr, name, value ? 1 : 0);
963d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy    }
964d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
965b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static void logd(String s) {
9667f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff        Log.d(TAG, s);
9677f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff    }
968d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy
9694c8982ad820007512e4e9cbb7f15925228d70761Irfan Sheriff    private static void loge(String s) {
9707f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff        Log.e(TAG, s);
9717f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff    }
972b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
973b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
974b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Bundle of good link count parameters
975b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
976b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static class GoodLinkTarget {
977b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public final int RSSI_ADJ_DBM;
978b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public final int SAMPLE_COUNT;
979b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public final int REDUCE_TIME_MS;
980b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public GoodLinkTarget(int adj, int count, int time) {
981b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            RSSI_ADJ_DBM = adj;
982b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            SAMPLE_COUNT = count;
983b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            REDUCE_TIME_MS = time;
984b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
985b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    }
986b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
987b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
988b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Bundle of max avoidance time parameters
989b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
990b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private static class MaxAvoidTime {
991b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public final int TIME_MS;
992b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public final int MIN_RSSI_DBM;
993b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public MaxAvoidTime(int time, int rssi) {
994b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            TIME_MS = time;
995b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            MIN_RSSI_DBM = rssi;
996b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
997b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    }
998b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
999b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
1000b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Volume-weighted Exponential Moving Average (V-EMA)
1001b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     *    - volume-weighted:  each update has its own weight (number of packets)
1002b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     *    - exponential:      O(1) time and O(1) space for both update and query
1003b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     *    - moving average:   reflect most recent results and expire old ones
1004b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
1005b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private class VolumeWeightedEMA {
1006b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private double mValue;
1007b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private double mVolume;
1008b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private double mProduct;
1009b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private final double mAlpha;
1010b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1011b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public VolumeWeightedEMA(double coefficient) {
1012b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mValue   = 0.0;
1013b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mVolume  = 0.0;
1014b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mProduct = 0.0;
1015b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mAlpha   = coefficient;
1016b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1017b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1018b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public void update(double newValue, int newVolume) {
1019b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (newVolume <= 0) return;
1020b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // core update formulas
1021b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            double newProduct = newValue * newVolume;
1022b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mProduct = mAlpha * newProduct + (1 - mAlpha) * mProduct;
1023b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mVolume  = mAlpha * newVolume  + (1 - mAlpha) * mVolume;
1024b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mValue   = mProduct / mVolume;
1025b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1026b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    }
1027b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1028b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    /**
1029b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     * Record (RSSI -> pakce loss %) mappings of one BSSID
1030b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng     */
1031b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    private class BssidStatistics {
1032b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1033b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* MAC address of this BSSID */
1034b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private final String mBssid;
1035b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1036b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* RSSI -> packet loss % mappings */
1037b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private VolumeWeightedEMA[] mEntries;
1038b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mRssiBase;
1039b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mEntriesSize;
1040b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1041b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* Target to send good link notification, set when poor link is detected */
1042b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mGoodLinkTargetRssi;
1043b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mGoodLinkTargetCount;
1044b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1045b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* Index of GOOD_LINK_TARGET array */
1046b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private int mGoodLinkTargetIndex;
1047b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1048b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* Timestamps of some last events */
1049b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private long mLastTimeSample;
1050b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private long mLastTimeGood;
1051b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private long mLastTimePoor;
1052b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1053b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /* Max time to avoid this BSSID */
1054b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        private long mBssidAvoidTimeMax;
1055b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1056b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1057b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * Constructor
1058b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         *
1059b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param bssid is the address of this BSSID
1060b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1061b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public BssidStatistics(String bssid) {
1062b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            this.mBssid = bssid;
1063b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mRssiBase = BSSID_STAT_RANGE_LOW_DBM;
1064b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mEntriesSize = BSSID_STAT_RANGE_HIGH_DBM - BSSID_STAT_RANGE_LOW_DBM + 1;
1065b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mEntries = new VolumeWeightedEMA[mEntriesSize];
1066b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            for (int i = 0; i < mEntriesSize; i++)
1067b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                mEntries[i] = new VolumeWeightedEMA(EXP_COEFFICIENT_RECORD);
1068b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1069b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1070b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1071b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * Update this BSSID cache
1072b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         *
1073b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param rssi is the RSSI
1074b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param value is the new instant loss value at this RSSI
1075b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param volume is the volume for this single update
1076b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1077b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public void updateLoss(int rssi, double value, int volume) {
1078b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (volume <= 0) return;
1079b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int index = rssi - mRssiBase;
1080b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (index < 0 || index >= mEntriesSize) return;
1081b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mEntries[index].update(value, volume);
1082b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) {
1083b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                DecimalFormat df = new DecimalFormat("#.##");
1084b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                logd("Cache updated: loss[" + rssi + "]=" + df.format(mEntries[index].mValue * 100)
1085b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        + "% volume=" + df.format(mEntries[index].mVolume));
1086b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            }
1087b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1088b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1089b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1090b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * Get preset loss if the cache has insufficient data, observed from experiments.
1091b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         *
1092b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param rssi is the input RSSI
1093b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @return preset loss of the given RSSI
1094b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1095b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public double presetLoss(int rssi) {
1096b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (rssi <= -90) return 1.0;
1097b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (rssi > 0) return 0.0;
1098b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1099b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (sPresetLoss == null) {
1100b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                // pre-calculate all preset losses only once, then reuse them
1101b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                final int size = 90;
1102b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                sPresetLoss = new double[size];
1103b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                for (int i = 0; i < size; i++) sPresetLoss[i] = 1.0 / Math.pow(90 - i, 1.5);
1104b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            }
1105b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            return sPresetLoss[-rssi];
1106b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1107b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1108b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1109b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * A poor link is detected, calculate a target RSSI to bring WiFi back.
1110b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         *
1111b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param rssi is the current RSSI
1112b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @return true iff the current BSSID should be avoided
1113b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1114b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public boolean poorLinkDetected(int rssi) {
1115b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd("Poor link detected, rssi=" + rssi);
1116b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1117b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            long now = SystemClock.elapsedRealtime();
1118b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            long lastGood = now - mLastTimeGood;
1119b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            long lastPoor = now - mLastTimePoor;
1120b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1121b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // reduce the difficulty of good link target if last avoidance was long time ago
1122b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            while (mGoodLinkTargetIndex > 0
1123b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    && lastPoor >= GOOD_LINK_TARGET[mGoodLinkTargetIndex - 1].REDUCE_TIME_MS)
1124b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                mGoodLinkTargetIndex--;
1125b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mGoodLinkTargetCount = GOOD_LINK_TARGET[mGoodLinkTargetIndex].SAMPLE_COUNT;
1126b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1127b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // scan for a target RSSI at which the link is good
1128b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int from = rssi + GOOD_LINK_RSSI_RANGE_MIN;
1129b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int to = rssi + GOOD_LINK_RSSI_RANGE_MAX;
1130b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mGoodLinkTargetRssi = findRssiTarget(from, to, GOOD_LINK_LOSS_THRESHOLD);
1131b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mGoodLinkTargetRssi += GOOD_LINK_TARGET[mGoodLinkTargetIndex].RSSI_ADJ_DBM;
1132b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (mGoodLinkTargetIndex < GOOD_LINK_TARGET.length - 1) mGoodLinkTargetIndex++;
1133b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1134b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // calculate max avoidance time to prevent avoiding forever
1135b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int p = 0, pmax = MAX_AVOID_TIME.length - 1;
1136b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            while (p < pmax && rssi >= MAX_AVOID_TIME[p + 1].MIN_RSSI_DBM) p++;
1137b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            long avoidMax = MAX_AVOID_TIME[p].TIME_MS;
1138b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1139b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // don't avoid if max avoidance time is 0 (RSSI is super high)
1140b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (avoidMax <= 0) return false;
1141b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1142b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // set max avoidance time, send poor link notification
1143b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mBssidAvoidTimeMax = now + avoidMax;
1144b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1145b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd("goodRssi=" + mGoodLinkTargetRssi + " goodCount=" + mGoodLinkTargetCount
1146b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    + " lastGood=" + lastGood + " lastPoor=" + lastPoor + " avoidMax=" + avoidMax);
1147b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1148b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            return true;
1149b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1150b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1151b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1152b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * A new BSSID is connected, recalculate target RSSI threshold
1153b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1154b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public void newLinkDetected() {
1155b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // if this BSSID is currently being avoided, the reuse those values
1156b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (mBssidAvoidTimeMax > 0) {
1157b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                if (DBG) logd("Previous avoidance still in effect, rssi=" + mGoodLinkTargetRssi
1158b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        + " count=" + mGoodLinkTargetCount);
1159b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                return;
1160b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            }
1161b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1162b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            // calculate a new RSSI threshold for new link verifying
1163b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int from = BSSID_STAT_RANGE_LOW_DBM;
1164b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int to = BSSID_STAT_RANGE_HIGH_DBM;
1165b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mGoodLinkTargetRssi = findRssiTarget(from, to, GOOD_LINK_LOSS_THRESHOLD);
1166b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mGoodLinkTargetCount = 1;
1167b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            mBssidAvoidTimeMax = SystemClock.elapsedRealtime() + MAX_AVOID_TIME[0].TIME_MS;
1168b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            if (DBG) logd("New link verifying target set, rssi=" + mGoodLinkTargetRssi + " count="
1169b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    + mGoodLinkTargetCount);
1170b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1171b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1172b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        /**
1173b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * Return the first RSSI within the range where loss[rssi] < threshold
1174b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         *
1175b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param from start scanning from this RSSI
1176b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param to stop scanning at this RSSI
1177b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @param threshold target threshold for scanning
1178b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         * @return target RSSI
1179b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng         */
1180b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        public int findRssiTarget(int from, int to, double threshold) {
1181b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            from -= mRssiBase;
1182b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            to -= mRssiBase;
1183b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int emptyCount = 0;
1184b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            int d = from < to ? 1 : -1;
1185b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            for (int i = from; i != to; i += d)
1186b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                // don't use a data point if it volume is too small (statistically unreliable)
1187b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                if (i >= 0 && i < mEntriesSize && mEntries[i].mVolume > 1.0) {
1188b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    emptyCount = 0;
1189b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (mEntries[i].mValue < threshold) {
1190b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        // scan target found
1191b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        int rssi = mRssiBase + i;
1192b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        if (DBG) {
1193b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            DecimalFormat df = new DecimalFormat("#.##");
1194b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            logd("Scan target found: rssi=" + rssi + " threshold="
1195b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    + df.format(threshold * 100) + "% value="
1196b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    + df.format(mEntries[i].mValue * 100) + "% volume="
1197b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    + df.format(mEntries[i].mVolume));
1198b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        }
1199b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        return rssi;
1200b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    }
1201b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                } else if (++emptyCount >= BSSID_STAT_EMPTY_COUNT) {
1202b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    // cache has insufficient data around this RSSI, use preset loss instead
1203b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    int rssi = mRssiBase + i;
1204b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    double lossPreset = presetLoss(rssi);
1205b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    if (lossPreset < threshold) {
1206b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        if (DBG) {
1207b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            DecimalFormat df = new DecimalFormat("#.##");
1208b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                            logd("Scan target found: rssi=" + rssi + " threshold="
1209b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    + df.format(threshold * 100) + "% value="
1210b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                                    + df.format(lossPreset * 100) + "% volume=preset");
1211b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        }
1212b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                        return rssi;
1213b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                    }
1214b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng                }
1215b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng
1216b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng            return mRssiBase + to;
1217b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng        }
1218b33227d23eb0ec3507192f94c2eee651a0f97783Yuhao Zheng    }
1219654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy}
1220