WifiWatchdogStateMachine.java revision 4ad39d6ac16961df0e7a3e4b4e7075aaa5202787
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 19d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.app.Notification; 20d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.app.NotificationManager; 21d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.app.PendingIntent; 22654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.BroadcastReceiver; 23654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.ContentResolver; 24654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.Context; 25654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.Intent; 26654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.content.IntentFilter; 27d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.content.res.Resources; 28654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.database.ContentObserver; 29654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.ConnectivityManager; 30654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.DnsPinger; 31654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.NetworkInfo; 32654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.net.Uri; 33654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.Message; 34654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.os.SystemClock; 354ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levyimport android.os.SystemProperties; 36654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.provider.Settings; 37d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport android.provider.Settings.Secure; 38654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport android.util.Slog; 39654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 40654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.Protocol; 41654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.State; 42654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport com.android.internal.util.StateMachine; 43654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 44654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.BufferedInputStream; 45654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.IOException; 46654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.InputStream; 47654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.io.PrintWriter; 48654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.net.HttpURLConnection; 49654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.net.URL; 50654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.HashSet; 51654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.List; 52654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levyimport java.util.Scanner; 53d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levyimport java.util.regex.Pattern; 54654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 55654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy/** 56654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi 57654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * network with multiple access points. After the framework successfully 58654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * connects to an access point, the watchdog verifies connectivity by 'pinging' 59654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * the configured DNS server using {@link DnsPinger}. 60654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * <p> 61654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * On DNS check failure, the BSSID is blacklisted if it is reasonably likely 62654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * that another AP might have internet access; otherwise the SSID is disabled. 63654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * <p> 64654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * On DNS success, the WatchdogService initiates a walled garden check via an 65654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * http get. A browser window is activated if a walled garden is detected. 66654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * 67654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @hide 68654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 69654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levypublic class WifiWatchdogStateMachine extends StateMachine { 70654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 71d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 72e046975503e7c6ebd78e35afaad88e3fb1ebfb5aIsaac Levy private static final boolean VDBG = false; 73654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final boolean DBG = true; 74654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final String WWSM_TAG = "WifiWatchdogStateMachine"; 75654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 76654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int WIFI_SIGNAL_LEVELS = 4; 77654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 78654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Low signal is defined as less than or equal to cut off 79654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 80654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int LOW_SIGNAL_CUTOFF = 1; 81654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 82d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000; 83d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 10 * 60 * 1000; 84d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000; 85654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 86d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7; 87d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final int DEFAULT_NUM_DNS_PINGS = 5; 88d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final int DEFAULT_MIN_DNS_RESPONSES = 3; 89654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final long DNS_PING_INTERVAL_MS = 100; 90654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 91d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 1500; 92d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 93d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000; 94d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 95d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final String DEFAULT_WALLED_GARDEN_URL = "http://www.google.com/"; 96d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final String DEFAULT_WALLED_GARDEN_PATTERN = "<title>.*Google.*</title>"; 97d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 98654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 99654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int BASE = Protocol.BASE_WIFI_WATCHDOG; 100654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 101654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 102654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Indicates the enable setting of WWS may have changed 103654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 104654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1; 105654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 106654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 107654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Indicates the wifi network state has changed. Passed w/ original intent 108654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * which has a non-null networkInfo object 109654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 110654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2; 111654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 112654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Indicates the signal has changed. Passed with arg1 113654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * {@link #mNetEventCounter} and arg2 [raw signal strength] 114654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 115654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int EVENT_RSSI_CHANGE = BASE + 3; 116654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4; 117654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5; 118d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6; 119654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 120654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int MESSAGE_CHECK_STEP = BASE + 100; 121654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 101; 122654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int MESSAGE_HANDLE_BAD_AP = BASE + 102; 123654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 124654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * arg1 == mOnlineWatchState.checkCount 125654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 126654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 103; 127654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 104; 128654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 129654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private Context mContext; 130654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private ContentResolver mContentResolver; 131654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WifiManager mWifiManager; 132654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private DnsPinger mDnsPinger; 133654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private IntentFilter mIntentFilter; 134654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private BroadcastReceiver mBroadcastReceiver; 135654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 136654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private DefaultState mDefaultState = new DefaultState(); 137654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState(); 138654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState(); 139654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private NotConnectedState mNotConnectedState = new NotConnectedState(); 140654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private ConnectedState mConnectedState = new ConnectedState(); 141654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private DnsCheckingState mDnsCheckingState = new DnsCheckingState(); 142654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private OnlineWatchState mOnlineWatchState = new OnlineWatchState(); 143654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState(); 144654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WalledGardenState mWalledGardenState = new WalledGardenState(); 145654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private BlacklistedApState mBlacklistedApState = new BlacklistedApState(); 146654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 147d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private long mDnsCheckShortIntervalMs; 148d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private long mDnsCheckLongIntervalMs; 149d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private long mWalledGardenIntervalMs; 150d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private int mMaxSsidBlacklists; 151d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private int mNumDnsPings; 152d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private int mMinDnsResponses; 153d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private int mDnsPingTimeoutMs; 154d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private long mBlacklistFollowupIntervalMs; 155d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private boolean mWalledGardenTestEnabled; 156d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private String mWalledGardenUrl; 157d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private Pattern mWalledGardenPattern; 158d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 159d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private boolean mShowDisabledNotification; 160654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 161654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * The {@link WifiInfo} object passed to WWSM on network broadcasts 162654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 163654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WifiInfo mInitialConnInfo; 164654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private int mNetEventCounter = 0; 165654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 166654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 167654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Currently maintained but not used, TODO 168654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 169654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private HashSet<String> mBssids = new HashSet<String>(); 170d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private int mNumCheckFailures = 0; 171654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 172654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private Long mLastWalledGardenCheckTime = null; 173654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 174654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 175654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * This is set by the blacklisted state and reset when connected to a new AP. 176654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * It triggers a disableNetwork call if a DNS check fails. 177654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 178654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean mDisableAPNextFailure = false; 1794ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy public ConnectivityManager mConnectivityManager; 180654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 181654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 182654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * STATE MAP 183654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Default 184654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * / \ 185654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Disabled Enabled 186654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * / \ 187d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * NotConnected Connected 188654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * /---------\ 189654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * (all other states) 190654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 191654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private WifiWatchdogStateMachine(Context context) { 192654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy super(WWSM_TAG); 193654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContext = context; 194654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContentResolver = context.getContentResolver(); 195654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 196654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context, 197654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy ConnectivityManager.TYPE_WIFI); 198654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 199654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy setupNetworkReceiver(); 200654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 201654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // The content observer to listen needs a handler 202654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy registerForSettingsChanges(); 203d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy registerForWatchdogToggle(); 204654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mDefaultState); 205654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mWatchdogDisabledState, mDefaultState); 206654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mWatchdogEnabledState, mDefaultState); 207654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mNotConnectedState, mWatchdogEnabledState); 208654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mConnectedState, mWatchdogEnabledState); 209654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mDnsCheckingState, mConnectedState); 210654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mDnsCheckFailureState, mConnectedState); 211654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mWalledGardenState, mConnectedState); 212654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mBlacklistedApState, mConnectedState); 213654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy addState(mOnlineWatchState, mConnectedState); 214654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 215654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy setInitialState(mWatchdogDisabledState); 216d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy updateSettings(); 217d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mShowDisabledNotification = getSettingsBoolean(mContentResolver, 218d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true); 219654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 220654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 221654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) { 2224ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy ContentResolver contentResolver = context.getContentResolver(); 2234ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy // Disable for wifi only devices. 2244ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null && 2254ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy "wifi-only".equals(SystemProperties.get("ro.carrier"))) { 2264ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false); 2274ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy } 228654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context); 229654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy wwsm.start(); 230654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED); 231654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return wwsm; 232654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 233654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 234654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 235654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * 236654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 237654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private void setupNetworkReceiver() { 238654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mBroadcastReceiver = new BroadcastReceiver() { 239654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 240654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void onReceive(Context context, Intent intent) { 241654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy String action = intent.getAction(); 242654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 243654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessage(EVENT_NETWORK_STATE_CHANGE, intent); 244654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { 245654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter, 246654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget(); 247654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 248654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessage(EVENT_SCAN_RESULTS_AVAILABLE); 249654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 250654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE, 251654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 252654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy WifiManager.WIFI_STATE_UNKNOWN)); 253654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 254654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 255654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy }; 256654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 257654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mIntentFilter = new IntentFilter(); 258654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 259654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 260654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 261654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 262654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 263654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 264654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 265654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Observes the watchdog on/off setting, and takes action when changed. 266654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 267d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private void registerForWatchdogToggle() { 268654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy ContentObserver contentObserver = new ContentObserver(this.getHandler()) { 269654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 270654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void onChange(boolean selfChange) { 271654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessage(EVENT_WATCHDOG_TOGGLED); 272654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 273654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy }; 274654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 275654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContext.getContentResolver().registerContentObserver( 276654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), 277654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy false, contentObserver); 278654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 279654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 280654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 281d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * Observes watchdogs secure setting changes. 282d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy */ 283d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private void registerForSettingsChanges() { 284d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy ContentObserver contentObserver = new ContentObserver(this.getHandler()) { 285d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy @Override 286d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy public void onChange(boolean selfChange) { 287d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy sendMessage(EVENT_WATCHDOG_SETTINGS_CHANGE); 288d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 289d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy }; 290d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 291d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 292d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor( 293d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS), 294d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 295d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 296d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS), 297d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 298d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 299d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS), 300d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 301d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 302d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS), 303d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 304d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 305d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS), 306d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 307d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 308d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES), 309d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 310d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 311d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS), 312d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 313d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 314d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor( 315d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS), 316d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 317d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 318d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED), 319d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 320d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 321d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL), 322d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 323d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mContext.getContentResolver().registerContentObserver( 324d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN), 325d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy false, contentObserver); 326d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 327d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 328d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy /** 329654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * DNS based detection techniques do not work at all hotspots. The one sure 330654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * way to check a walled garden is to see if a URL fetch on a known address 331654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * fetches the data we expect 332654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 333654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private boolean isWalledGardenConnection() { 334654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy InputStream in = null; 335654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy HttpURLConnection urlConnection = null; 336654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy try { 337d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy URL url = new URL(mWalledGardenUrl); 338654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy urlConnection = (HttpURLConnection) url.openConnection(); 339654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy in = new BufferedInputStream(urlConnection.getInputStream()); 340654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Scanner scanner = new Scanner(in); 341d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (scanner.findInLine(mWalledGardenPattern) != null) { 342654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return false; 343654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 344654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return true; 345654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 346654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } catch (IOException e) { 347654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return false; 348654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } finally { 349654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (in != null) { 350654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy try { 351654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy in.close(); 352654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } catch (IOException e) { 353654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 354654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 355654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (urlConnection != null) 356654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy urlConnection.disconnect(); 357654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 358654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 359654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 360654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private boolean rssiStrengthAboveCutoff(int rssi) { 361654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF; 362654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 363654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 364654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void dump(PrintWriter pw) { 365654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy pw.print("WatchdogStatus: "); 366654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy pw.print("State " + getCurrentState()); 367654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy pw.println(", network [" + mInitialConnInfo + "]"); 368d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy pw.print("checkFailures " + mNumCheckFailures); 369654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy pw.println(", bssids: " + mBssids); 370654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime); 371654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 372654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 373654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private boolean isWatchdogEnabled() { 374d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return getSettingsBoolean(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true); 375654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 376654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 377d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private void updateSettings() { 378d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver, 379d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS, 380d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS); 381d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver, 382d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS, 383d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_DNS_CHECK_LONG_INTERVAL_MS); 384d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mMaxSsidBlacklists = Secure.getInt(mContentResolver, 385d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS, 386d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_MAX_SSID_BLACKLISTS); 387d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mNumDnsPings = Secure.getInt(mContentResolver, 388d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_NUM_DNS_PINGS, 389d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_NUM_DNS_PINGS); 390d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mMinDnsResponses = Secure.getInt(mContentResolver, 391d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES, 392d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_MIN_DNS_RESPONSES); 393d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsPingTimeoutMs = Secure.getInt(mContentResolver, 394d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS, 395d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_DNS_PING_TIMEOUT_MS); 396d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver, 397d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS, 398d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS); 399d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver, 400d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true); 401d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mWalledGardenUrl = getSettingsStr(mContentResolver, 402d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL, 403d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_WALLED_GARDEN_URL); 404d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mWalledGardenPattern = Pattern.compile(getSettingsStr(mContentResolver, 405d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN, 406d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_WALLED_GARDEN_PATTERN)); 407d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mWalledGardenIntervalMs = Secure.getLong(mContentResolver, 408d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS, 409d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy DEFAULT_WALLED_GARDEN_INTERVAL_MS); 410d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 411654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 412654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 413654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Helper to return wait time left given a min interval and last run 414654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * 415654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @param interval minimum wait interval 416654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @param lastTime last time action was performed in 417654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * SystemClock.elapsedRealtime(). Null if never. 418654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @return non negative time to wait 419654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 420654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static long waitTime(long interval, Long lastTime) { 421654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (lastTime == null) 422654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return 0; 423654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy long wait = interval + lastTime - SystemClock.elapsedRealtime(); 424654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return wait > 0 ? wait : 0; 425654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 426654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 427654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private static String wifiInfoToStr(WifiInfo wifiInfo) { 428654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (wifiInfo == null) 429654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return "null"; 430654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")"; 431654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 432654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 433654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 434654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * 435654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 436654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private void resetWatchdogState() { 437654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mInitialConnInfo = null; 438654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mDisableAPNextFailure = false; 439654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mLastWalledGardenCheckTime = null; 440d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mNumCheckFailures = 0; 441654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mBssids.clear(); 442654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 443654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 444654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private void popUpBrowser() { 445654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Uri uri = Uri.parse("http://www.google.com"); 446654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Intent intent = new Intent(Intent.ACTION_VIEW, uri); 447654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | 448654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Intent.FLAG_ACTIVITY_NEW_TASK); 449654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContext.startActivity(intent); 450654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 451654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 452d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private void displayDisabledNetworkNotification() { 453d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Resources r = Resources.getSystem(); 454d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy CharSequence title = 455d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled); 456d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy CharSequence msg = 457d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy r.getText(com.android.internal.R.string.wifi_watchdog_network_disabled_detailed); 458d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 459d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Notification wifiDisabledWarning = new Notification.Builder(mContext) 460d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) 461d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setDefaults(Notification.DEFAULT_ALL) 462d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setTicker(title) 463d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setContentTitle(title) 464d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setContentText(msg) 465d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setContentIntent(PendingIntent.getActivity(mContext, 0, 466d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy new Intent(Settings.ACTION_WIFI_IP_SETTINGS) 467d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0)) 468d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setWhen(System.currentTimeMillis()) 469d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .setAutoCancel(true) 470d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .getNotification(); 471d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 472d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy NotificationManager notificationManager = (NotificationManager) mContext 473d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy .getSystemService(Context.NOTIFICATION_SERVICE); 474d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 475d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy notificationManager.notify("WifiWatchdog", wifiDisabledWarning.icon, wifiDisabledWarning); 476654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 477654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 4784ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy /** 4794ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy * @return true if there is definitely no mobile data (we'll be less aggressive) 4804ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy */ 4814ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy private boolean hasNoMobileData() { 4824ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy if (mConnectivityManager == null) { 4834ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy mConnectivityManager = (ConnectivityManager) mContext.getSystemService( 4844ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy Context.CONNECTIVITY_SERVICE); 4854ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy } 4864ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy NetworkInfo mobileNetInfo = mConnectivityManager.getNetworkInfo( 4874ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy ConnectivityManager.TYPE_MOBILE); 4884ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy if (mobileNetInfo == null || !mobileNetInfo.isAvailable()) { 4894ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy return true; 4904ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy } 4914ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy return false; 4924ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy } 4934ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy 494654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class DefaultState extends State { 495654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 496654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 497d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy switch (msg.what) { 498d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy case EVENT_WATCHDOG_SETTINGS_CHANGE: 499d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy updateSettings(); 500d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (VDBG) { 501d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Slog.d(WWSM_TAG, "Updating wifi-watchdog secure settings"); 502d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 503d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return HANDLED; 504d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 505654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 506654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Caught message " + msg.what + " in state " + 507654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy getCurrentState().getName()); 508654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 509654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 510654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 511654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 512654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 513654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class WatchdogDisabledState extends State { 514654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 515654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 516654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy switch (msg.what) { 517654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_WATCHDOG_TOGGLED: 518654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (isWatchdogEnabled()) 519654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mNotConnectedState); 520654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 521654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 522654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 523654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 524654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 525654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 526654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class WatchdogEnabledState extends State { 527654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 528654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 529654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy resetWatchdogState(); 530654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); 531654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.i(WWSM_TAG, "WifiWatchdogService enabled"); 532654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 533654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 534654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 535654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 536654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy switch (msg.what) { 537654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_WATCHDOG_TOGGLED: 538654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (!isWatchdogEnabled()) 539654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mWatchdogDisabledState); 540654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 541654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_NETWORK_STATE_CHANGE: 542654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Intent stateChangeIntent = (Intent) msg.obj; 543654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy NetworkInfo networkInfo = (NetworkInfo) 544654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 545654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 546654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy switch (networkInfo.getState()) { 547654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case CONNECTED: 548654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // WifiInfo wifiInfo = (WifiInfo) 549654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // stateChangeIntent 550654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // .getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); 551654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // TODO : Replace with above code when API is changed 552654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 553654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (wifiInfo == null) { 554654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.e(WWSM_TAG, "Connected --> WifiInfo object null!"); 555654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 556654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 557654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 558654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) { 559654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.e(WWSM_TAG, "Received wifiInfo object with null elts: " 560654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy + wifiInfoToStr(wifiInfo)); 561654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 562654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 563654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 564654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy initConnection(wifiInfo); 565654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mDnsCheckingState); 566654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mNetEventCounter++; 567654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 568654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case DISCONNECTED: 569654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case DISCONNECTING: 570654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mNetEventCounter++; 571654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mNotConnectedState); 572654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 573654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 574654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 575654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_WIFI_RADIO_STATE_CHANGE: 576654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) { 577654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.i(WWSM_TAG, "WifiStateDisabling -- Resetting WatchdogState"); 578654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy resetWatchdogState(); 579654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mNetEventCounter++; 580654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mNotConnectedState); 581654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 582654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 583654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 584654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 585654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 586654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 587654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 588654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 589654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * @param wifiInfo Info object with non-null ssid and bssid 590654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 591654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private void initConnection(WifiInfo wifiInfo) { 592654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 593654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Connected:: old " + wifiInfoToStr(mInitialConnInfo) + 594654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy " ==> new " + wifiInfoToStr(wifiInfo)); 595654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 596654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 597654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (mInitialConnInfo == null || !wifiInfo.getSSID().equals(mInitialConnInfo.getSSID())) { 598654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy resetWatchdogState(); 599654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else if (!wifiInfo.getBSSID().equals(mInitialConnInfo.getBSSID())) { 600654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mDisableAPNextFailure = false; 601654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 602654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mInitialConnInfo = wifiInfo; 603654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 604654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 605654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 606654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void exit() { 607654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mContext.unregisterReceiver(mBroadcastReceiver); 608654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.i(WWSM_TAG, "WifiWatchdogService disabled"); 609654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 610654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 611654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 612654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class NotConnectedState extends State { 613654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 614654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 615654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class ConnectedState extends State { 616654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 617654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 618654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy switch (msg.what) { 619654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_SCAN_RESULTS_AVAILABLE: 620654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy String curSsid = mInitialConnInfo.getSSID(); 621654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy List<ScanResult> results = mWifiManager.getScanResults(); 622654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy int oldNumBssids = mBssids.size(); 623654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 624654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (results == null) { 625654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 626654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "updateBssids: Got null scan results!"); 627654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 628654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 629654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 630654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 631654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy for (ScanResult result : results) { 632654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (result == null || result.SSID == null) { 633654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 634654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Received invalid scan result: " + result); 635654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 636654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy continue; 637654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 638654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (curSsid.equals(result.SSID)) 639654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mBssids.add(result.BSSID); 640654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 641654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 642d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy case EVENT_WATCHDOG_SETTINGS_CHANGE: 643d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy // Stop current checks, but let state update 644d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy transitionTo(mOnlineWatchState); 645d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return NOT_HANDLED; 646654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 647654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 648654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 649654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 650654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 651654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 652654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class DnsCheckingState extends State { 653654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy int dnsCheckTries = 0; 654654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy int dnsCheckSuccesses = 0; 655654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy String dnsCheckLogStr = ""; 656654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 657654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 658654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 659654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckSuccesses = 0; 660654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckTries = 0; 661654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 662654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime()); 663d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ", 664d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsPinger.getDns(), mInitialConnInfo.getSSID()); 665654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 666654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 667654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendCheckStepMessage(0); 668654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 669654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 670654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 671654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 672654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.what != MESSAGE_CHECK_STEP) { 673654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 674654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 675654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != mNetEventCounter) { 676654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Check step out of sync, ignoring..."); 677654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 678654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 679654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 680654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), 681d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsPingTimeoutMs); 682654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 683654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckTries++; 684654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (pingResponseTime >= 0) 685654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckSuccesses++; 686654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 687654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 688654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (pingResponseTime >= 0) { 689654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckLogStr += "|" + pingResponseTime; 690654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 691654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy dnsCheckLogStr += "|x"; 692654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 693654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 694654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 695654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 696654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, dnsCheckLogStr); 697654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 698654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 699654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 700654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * After a full ping count, if we have more responses than this 701654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * cutoff, the outcome is success; else it is 'failure'. 702654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 703654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 704654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 705654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Our final success count will be at least this big, so we're 706654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * guaranteed to succeed. 707654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 708d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (dnsCheckSuccesses >= mMinDnsResponses) { 709654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // DNS CHECKS OK, NOW WALLED GARDEN 710654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 711654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, dnsCheckLogStr + "| SUCCESS"); 712654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 713654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 714654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (!shouldCheckWalledGarden()) { 715654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mOnlineWatchState); 716654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 717654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 718654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 719654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mLastWalledGardenCheckTime = SystemClock.elapsedRealtime(); 720654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (isWalledGardenConnection()) { 721654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) 722654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, 723654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy "Walled garden test complete - walled garden detected"); 724654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mWalledGardenState); 725654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 726654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) 727654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Walled garden test complete - online"); 728654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mOnlineWatchState); 729654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 730654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 731654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 732654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 733654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 734654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Our final count will be at most the current count plus the 735654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * remaining pings - we're guaranteed to fail. 736654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 737d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy int remainingChecks = mNumDnsPings - dnsCheckTries; 738d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (remainingChecks + dnsCheckSuccesses < mMinDnsResponses) { 739654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 740654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, dnsCheckLogStr + "| FAILURE"); 741654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 742654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mDnsCheckFailureState); 743654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 744654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 745654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 746654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // Still in dns check step 747654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendCheckStepMessage(DNS_PING_INTERVAL_MS); 748654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 749654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 750654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 751654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private boolean shouldCheckWalledGarden() { 752d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (!mWalledGardenTestEnabled) { 753654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) 754654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Skipping walled garden check - disabled"); 755654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return false; 756654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 757d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy long waitTime = waitTime(mWalledGardenIntervalMs, 758654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mLastWalledGardenCheckTime); 759654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (waitTime > 0) { 760654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 761654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Skipping walled garden check - wait " + 762654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy waitTime + " ms."); 763654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 764654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return false; 765654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 766654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return true; 767654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 768654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 769d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private void sendCheckStepMessage(long delay) { 770d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay); 771d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 772d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 773654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 774654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 775654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class OnlineWatchState extends State { 776654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 777654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * Signals a short-wait message is enqueued for the current 'guard' counter 778654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 779654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy boolean unstableSignalChecks = false; 780654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 781654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 782654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * The signal is unstable. We should enqueue a short-wait check, if one is enqueued 783654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * already 784654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 785654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy boolean signalUnstable = false; 786654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 787654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 788654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * A monotonic counter to ensure that at most one check message will be processed from any 789654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * set of check messages currently enqueued. Avoids duplicate checks when a low-signal 790654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy * event is observed. 791654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 792654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy int checkGuard = 0; 793654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Long lastCheckTime = null; 794654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 795654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 796654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 797654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy lastCheckTime = SystemClock.elapsedRealtime(); 798654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy signalUnstable = false; 799654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy checkGuard++; 800654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy unstableSignalChecks = false; 801654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy triggerSingleDnsCheck(); 802654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 803654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 804654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 805654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 806654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy switch (msg.what) { 807654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case EVENT_RSSI_CHANGE: 808654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != mNetEventCounter) { 809654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 810654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Rssi change message out of sync, ignoring"); 811654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 812654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 813654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 814654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy int newRssi = msg.arg2; 815654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy signalUnstable = !rssiStrengthAboveCutoff(newRssi); 816654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 817654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "OnlineWatchState:: new rssi " + newRssi + " --> level " + 818654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS)); 819654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 820654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 821654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (signalUnstable && !unstableSignalChecks) { 822654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 823654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Sending triggered check msg"); 824654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 825654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy triggerSingleDnsCheck(); 826654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 827654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 828654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy case MESSAGE_SINGLE_DNS_CHECK: 829654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != checkGuard) { 830654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 831654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Single check msg out of sync, ignoring."); 832654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 833654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 834654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 835654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy lastCheckTime = SystemClock.elapsedRealtime(); 836654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), 837d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mDnsPingTimeoutMs); 838654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (responseTime >= 0) { 839654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 840654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: " 841654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy + responseTime); 842654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 843654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 844654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy checkGuard++; 845654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy unstableSignalChecks = false; 846654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy triggerSingleDnsCheck(); 847654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 848654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (DBG) { 849654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks."); 850654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 851654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mDnsCheckingState); 852654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 853654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 854654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 855654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 856654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 857654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 858654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy /** 859d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * Times a dns check with an interval based on {@link #signalUnstable} 860654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy */ 861654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy private void triggerSingleDnsCheck() { 862654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy long waitInterval; 863654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (signalUnstable) { 864d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy waitInterval = mDnsCheckShortIntervalMs; 865654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy unstableSignalChecks = true; 866654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 867d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy waitInterval = mDnsCheckLongIntervalMs; 868654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 869654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0), 870654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy waitTime(waitInterval, lastCheckTime)); 871654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 872654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 873654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 874654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class DnsCheckFailureState extends State { 8754ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy 876654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 877654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 878d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mNumCheckFailures++; 879654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget(); 880654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 881654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 882654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 883654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 884654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.what != MESSAGE_HANDLE_BAD_AP) { 885654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 886654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 887654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 888654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != mNetEventCounter) { 889654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 890654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "Msg out of sync, ignoring..."); 891654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 892654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 893654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 894654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 895d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (mDisableAPNextFailure || mNumCheckFailures >= mMaxSsidBlacklists) { 8964ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy if (hasNoMobileData()) { 8974ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy Slog.w(WWSM_TAG, "Would disable bad network, but device has no mobile data!" + 8984ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy " Going idle..."); 8994ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy // This state should be called idle -- will be changing flow. 9004ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy transitionTo(mNotConnectedState); 9014ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy return HANDLED; 9024ad39d6ac16961df0e7a3e4b4e7075aaa5202787Isaac Levy } 903654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy // TODO : Unban networks if they had low signal ? 904654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.i(WWSM_TAG, "Disabling current SSID " + wifiInfoToStr(mInitialConnInfo) 905d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy + ". " + "numCheckFailures " + mNumCheckFailures 906d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy + ", numAPs " + mBssids.size()); 907654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mWifiManager.disableNetwork(mInitialConnInfo.getNetworkId()); 908d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy if (mShowDisabledNotification) { 909d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy displayDisabledNetworkNotification(); 910d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mShowDisabledNotification = false; 911d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy putSettingsBoolean(mContentResolver, 912d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, false); 913d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 914654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mNotConnectedState); 915654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } else { 916d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy Slog.i(WWSM_TAG, "Blacklisting current BSSID. " + wifiInfoToStr(mInitialConnInfo) 917d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy + "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size()); 918654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 919654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mWifiManager.addToBlacklist(mInitialConnInfo.getBSSID()); 920654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mWifiManager.reassociate(); 921654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mBlacklistedApState); 922654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 923654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 924654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 925654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 926654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 927654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class WalledGardenState extends State { 928654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 929654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 930654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget(); 931654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 932654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 933654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 934654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 935654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) { 936654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 937654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 938654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 939654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != mNetEventCounter) { 940654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 941654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "WalledGardenState::Msg out of sync, ignoring..."); 942654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 943654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 944654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 945654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy popUpBrowser(); 946654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mOnlineWatchState); 947654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 948654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 949654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 950654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 951654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy class BlacklistedApState extends State { 952654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 953654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public void enter() { 954654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy mDisableAPNextFailure = true; 955654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0), 956d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy mBlacklistFollowupIntervalMs); 957654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 958654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 959654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy @Override 960654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy public boolean processMessage(Message msg) { 961654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.what != MESSAGE_NETWORK_FOLLOWUP) { 962654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return NOT_HANDLED; 963654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 964654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 965654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (msg.arg1 != mNetEventCounter) { 966654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy if (VDBG) { 967654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy Slog.v(WWSM_TAG, "BlacklistedApState::Msg out of sync, ignoring..."); 968654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 969654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 970654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 971654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy 972654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy transitionTo(mDnsCheckingState); 973654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy return HANDLED; 974654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 975654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy } 976d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 977d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 978d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy /** 979d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * Convenience function for retrieving a single secure settings value 980d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * as a string with a default value. 981d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * 982d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param cr The ContentResolver to access. 983d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param name The name of the setting to retrieve. 984d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param def Value to return if the setting is not defined. 985d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * 986d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @return The setting's current value, or 'def' if it is not defined 987d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy */ 988d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static String getSettingsStr(ContentResolver cr, String name, String def) { 989d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy String v = Settings.Secure.getString(cr, name); 990d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return v != null ? v : def; 991d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 992d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 993d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy /** 994d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * Convenience function for retrieving a single secure settings value 995d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * as a boolean. Note that internally setting values are always 996d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * stored as strings; this function converts the string to a boolean 997d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * for you. The default value will be returned if the setting is 998d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * not defined or not a valid boolean. 999d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * 1000d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param cr The ContentResolver to access. 1001d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param name The name of the setting to retrieve. 1002d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param def Value to return if the setting is not defined. 1003d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * 1004d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @return The setting's current value, or 'def' if it is not defined 1005d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * or not a valid boolean. 1006d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy */ 1007d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static boolean getSettingsBoolean(ContentResolver cr, String name, boolean def) { 1008d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return Settings.Secure.getInt(cr, name, def ? 1 : 0) == 1; 1009d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 1010d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 1011d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy /** 1012d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * Convenience function for updating a single settings value as an 1013d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * integer. This will either create a new entry in the table if the 1014d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * given name does not exist, or modify the value of the existing row 1015d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * with that name. Note that internally setting values are always 1016d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * stored as strings, so this function converts the given value to a 1017d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * string before storing it. 1018d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * 1019d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param cr The ContentResolver to access. 1020d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param name The name of the setting to modify. 1021d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @param value The new value for the setting. 1022d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy * @return true if the value was set, false on database errors 1023d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy */ 1024d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy private static boolean putSettingsBoolean(ContentResolver cr, String name, boolean value) { 1025d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy return Settings.Secure.putInt(cr, name, value ? 1 : 0); 1026d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy } 1027d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 1028d7b3e6a39b6b2e155b24ef470023bafb3b9fa35aIsaac Levy 1029654f5090754e4e1bf4c1736d0a24769a15a6037eIsaac Levy} 1030