- Keep the walled garden detection in place Bug: 5719771 Change-Id: I9e5d69b6d56096be904106b1db48c02db4c0fb50
1162 lines
46 KiB
Java
1162 lines
46 KiB
Java
/*
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.net.wifi;
|
|
|
|
import android.app.Notification;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.res.Resources;
|
|
import android.database.ContentObserver;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.DnsPinger;
|
|
import android.net.NetworkInfo;
|
|
import android.net.Uri;
|
|
import android.os.Message;
|
|
import android.os.SystemClock;
|
|
import android.os.SystemProperties;
|
|
import android.provider.Settings;
|
|
import android.provider.Settings.Secure;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.R;
|
|
import com.android.internal.util.Protocol;
|
|
import com.android.internal.util.State;
|
|
import com.android.internal.util.StateMachine;
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.InetAddress;
|
|
import java.net.URL;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
|
|
* network with multiple access points. After the framework successfully
|
|
* connects to an access point, the watchdog verifies connectivity by 'pinging'
|
|
* the configured DNS server using {@link DnsPinger}.
|
|
* <p>
|
|
* On DNS check failure, the BSSID is blacklisted if it is reasonably likely
|
|
* that another AP might have internet access; otherwise the SSID is disabled.
|
|
* <p>
|
|
* On DNS success, the WatchdogService initiates a walled garden check via an
|
|
* http get. A browser window is activated if a walled garden is detected.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class WifiWatchdogStateMachine extends StateMachine {
|
|
|
|
private static final boolean DBG = false;
|
|
private static final String TAG = "WifiWatchdogStateMachine";
|
|
private static final String DISABLED_NETWORK_NOTIFICATION_ID = "WifiWatchdog.networkdisabled";
|
|
private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden";
|
|
|
|
private static final int WIFI_SIGNAL_LEVELS = 4;
|
|
/**
|
|
* Low signal is defined as less than or equal to cut off
|
|
*/
|
|
private static final int LOW_SIGNAL_CUTOFF = 0;
|
|
|
|
private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
|
|
private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 60 * 60 * 1000;
|
|
private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
|
|
|
|
private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
|
|
private static final int DEFAULT_NUM_DNS_PINGS = 5; // Multiple pings to detect setup issues
|
|
private static final int DEFAULT_MIN_DNS_RESPONSES = 1;
|
|
|
|
private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
|
|
|
|
private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
|
|
|
|
// See http://go/clientsdns for usage approval
|
|
private static final String DEFAULT_WALLED_GARDEN_URL =
|
|
"http://clients3.google.com/generate_204";
|
|
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
|
|
|
|
/* Some carrier apps might have support captive portal handling. Add some delay to allow
|
|
app authentication to be done before our test.
|
|
TODO: This should go away once we provide an API to apps to disable walled garden test
|
|
for certain SSIDs
|
|
*/
|
|
private static final int WALLED_GARDEN_START_DELAY_MS = 3000;
|
|
|
|
private static final int DNS_INTRATEST_PING_INTERVAL_MS = 200;
|
|
/* With some router setups, it takes a few hunder milli-seconds before connection is active */
|
|
private static final int DNS_START_DELAY_MS = 1000;
|
|
|
|
private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
|
|
|
|
/**
|
|
* Indicates the enable setting of WWS may have changed
|
|
*/
|
|
private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1;
|
|
|
|
/**
|
|
* Indicates the wifi network state has changed. Passed w/ original intent
|
|
* which has a non-null networkInfo object
|
|
*/
|
|
private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
|
|
/**
|
|
* Indicates the signal has changed. Passed with arg1
|
|
* {@link #mNetEventCounter} and arg2 [raw signal strength]
|
|
*/
|
|
private static final int EVENT_RSSI_CHANGE = BASE + 3;
|
|
private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4;
|
|
private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
|
|
private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6;
|
|
|
|
private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 100;
|
|
private static final int MESSAGE_HANDLE_BAD_AP = BASE + 101;
|
|
/**
|
|
* arg1 == mOnlineWatchState.checkCount
|
|
*/
|
|
private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 102;
|
|
private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 103;
|
|
private static final int MESSAGE_DELAYED_WALLED_GARDEN_CHECK = BASE + 104;
|
|
|
|
private Context mContext;
|
|
private ContentResolver mContentResolver;
|
|
private WifiManager mWifiManager;
|
|
private DnsPinger mDnsPinger;
|
|
private IntentFilter mIntentFilter;
|
|
private BroadcastReceiver mBroadcastReceiver;
|
|
|
|
private DefaultState mDefaultState = new DefaultState();
|
|
private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
|
|
private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
|
|
private NotConnectedState mNotConnectedState = new NotConnectedState();
|
|
private ConnectedState mConnectedState = new ConnectedState();
|
|
private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
|
|
private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
|
|
private OnlineState mOnlineState = new OnlineState();
|
|
private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
|
|
private DelayWalledGardenState mDelayWalledGardenState = new DelayWalledGardenState();
|
|
private WalledGardenState mWalledGardenState = new WalledGardenState();
|
|
private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
|
|
|
|
private long mDnsCheckShortIntervalMs;
|
|
private long mDnsCheckLongIntervalMs;
|
|
private long mWalledGardenIntervalMs;
|
|
private int mMaxSsidBlacklists;
|
|
private int mNumDnsPings;
|
|
private int mMinDnsResponses;
|
|
private int mDnsPingTimeoutMs;
|
|
private long mBlacklistFollowupIntervalMs;
|
|
private boolean mPoorNetworkDetectionEnabled;
|
|
private boolean mWalledGardenTestEnabled;
|
|
private String mWalledGardenUrl;
|
|
|
|
private boolean mShowDisabledNotification;
|
|
/**
|
|
* The {@link WifiInfo} object passed to WWSM on network broadcasts
|
|
*/
|
|
private WifiInfo mConnectionInfo;
|
|
private int mNetEventCounter = 0;
|
|
|
|
/**
|
|
* Currently maintained but not used, TODO
|
|
*/
|
|
private HashSet<String> mBssids = new HashSet<String>();
|
|
private int mNumCheckFailures = 0;
|
|
|
|
private Long mLastWalledGardenCheckTime = null;
|
|
|
|
/**
|
|
* This is set by the blacklisted state and reset when connected to a new AP.
|
|
* It triggers a disableNetwork call if a DNS check fails.
|
|
*/
|
|
public boolean mDisableAPNextFailure = false;
|
|
private static boolean sWifiOnly = false;
|
|
private boolean mDisabledNotificationShown;
|
|
private boolean mWalledGardenNotificationShown;
|
|
public boolean mHasConnectedWifiManager = false;
|
|
|
|
/**
|
|
* STATE MAP
|
|
* Default
|
|
* / \
|
|
* Disabled Enabled
|
|
* / \
|
|
* NotConnected Connected
|
|
* /---------\
|
|
* (all other states)
|
|
*/
|
|
private WifiWatchdogStateMachine(Context context) {
|
|
super(TAG);
|
|
mContext = context;
|
|
mContentResolver = context.getContentResolver();
|
|
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
|
mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
|
|
this.getHandler().getLooper(), this.getHandler(),
|
|
ConnectivityManager.TYPE_WIFI);
|
|
|
|
setupNetworkReceiver();
|
|
|
|
// The content observer to listen needs a handler
|
|
registerForSettingsChanges();
|
|
registerForWatchdogToggle();
|
|
addState(mDefaultState);
|
|
addState(mWatchdogDisabledState, mDefaultState);
|
|
addState(mWatchdogEnabledState, mDefaultState);
|
|
addState(mNotConnectedState, mWatchdogEnabledState);
|
|
addState(mConnectedState, mWatchdogEnabledState);
|
|
addState(mDnsCheckingState, mConnectedState);
|
|
addState(mDnsCheckFailureState, mConnectedState);
|
|
addState(mDelayWalledGardenState, mConnectedState);
|
|
addState(mWalledGardenState, mConnectedState);
|
|
addState(mBlacklistedApState, mConnectedState);
|
|
addState(mOnlineWatchState, mConnectedState);
|
|
addState(mOnlineState, mConnectedState);
|
|
|
|
setInitialState(mWatchdogDisabledState);
|
|
updateSettings();
|
|
}
|
|
|
|
public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
|
|
ContentResolver contentResolver = context.getContentResolver();
|
|
|
|
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
|
|
Context.CONNECTIVITY_SERVICE);
|
|
sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
|
|
|
|
// Disable for wifi only devices.
|
|
if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
|
|
sWifiOnly) {
|
|
putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
|
|
}
|
|
WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
|
|
wwsm.start();
|
|
wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
|
|
return wwsm;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
private void setupNetworkReceiver() {
|
|
mBroadcastReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
String action = intent.getAction();
|
|
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
|
|
sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
|
|
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
|
|
obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
|
|
intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
|
|
} else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
|
|
sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
|
|
} else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
|
|
sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
|
|
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
|
|
WifiManager.WIFI_STATE_UNKNOWN));
|
|
}
|
|
}
|
|
};
|
|
|
|
mIntentFilter = new IntentFilter();
|
|
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
|
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
|
|
mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
|
|
mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
|
|
}
|
|
|
|
/**
|
|
* Observes the watchdog on/off setting, and takes action when changed.
|
|
*/
|
|
private void registerForWatchdogToggle() {
|
|
ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
|
|
@Override
|
|
public void onChange(boolean selfChange) {
|
|
sendMessage(EVENT_WATCHDOG_TOGGLED);
|
|
}
|
|
};
|
|
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
|
|
false, contentObserver);
|
|
}
|
|
|
|
/**
|
|
* Observes watchdogs secure setting changes.
|
|
*/
|
|
private void registerForSettingsChanges() {
|
|
ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
|
|
@Override
|
|
public void onChange(boolean selfChange) {
|
|
sendMessage(EVENT_WATCHDOG_SETTINGS_CHANGE);
|
|
}
|
|
};
|
|
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(
|
|
Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(
|
|
Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
|
|
false, contentObserver);
|
|
mContext.getContentResolver().registerContentObserver(
|
|
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP)
|
|
, false, contentObserver);
|
|
}
|
|
|
|
/**
|
|
* DNS based detection techniques do not work at all hotspots. The one sure
|
|
* way to check a walled garden is to see if a URL fetch on a known address
|
|
* fetches the data we expect
|
|
*/
|
|
private boolean isWalledGardenConnection() {
|
|
HttpURLConnection urlConnection = null;
|
|
try {
|
|
URL url = new URL(mWalledGardenUrl);
|
|
urlConnection = (HttpURLConnection) url.openConnection();
|
|
urlConnection.setInstanceFollowRedirects(false);
|
|
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
|
|
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
|
|
urlConnection.setUseCaches(false);
|
|
urlConnection.getInputStream();
|
|
// We got a valid response, but not from the real google
|
|
return urlConnection.getResponseCode() != 204;
|
|
} catch (IOException e) {
|
|
if (DBG) {
|
|
log("Walled garden check - probably not a portal: exception " + e);
|
|
}
|
|
return false;
|
|
} finally {
|
|
if (urlConnection != null) {
|
|
urlConnection.disconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean rssiStrengthAboveCutoff(int rssi) {
|
|
return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
|
|
}
|
|
|
|
public void dump(PrintWriter pw) {
|
|
pw.print("WatchdogStatus: ");
|
|
pw.print("State " + getCurrentState());
|
|
pw.println(", network [" + mConnectionInfo + "]");
|
|
pw.print("checkFailures " + mNumCheckFailures);
|
|
pw.println(", bssids: " + mBssids);
|
|
pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
|
|
}
|
|
|
|
private boolean isWatchdogEnabled() {
|
|
return getSettingsBoolean(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true);
|
|
}
|
|
|
|
private void updateSettings() {
|
|
mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS,
|
|
DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS);
|
|
mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS,
|
|
DEFAULT_DNS_CHECK_LONG_INTERVAL_MS);
|
|
mMaxSsidBlacklists = Secure.getInt(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS,
|
|
DEFAULT_MAX_SSID_BLACKLISTS);
|
|
mNumDnsPings = Secure.getInt(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_NUM_DNS_PINGS,
|
|
DEFAULT_NUM_DNS_PINGS);
|
|
mMinDnsResponses = Secure.getInt(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES,
|
|
DEFAULT_MIN_DNS_RESPONSES);
|
|
mDnsPingTimeoutMs = Secure.getInt(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS,
|
|
DEFAULT_DNS_PING_TIMEOUT_MS);
|
|
mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver,
|
|
Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS,
|
|
DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS);
|
|
//TODO: enable this by default after changing watchdog behavior
|
|
//Also, update settings description
|
|
mPoorNetworkDetectionEnabled = getSettingsBoolean(mContentResolver,
|
|
Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
|
|
mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
|
|
Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
|
|
mWalledGardenUrl = getSettingsStr(mContentResolver,
|
|
Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL,
|
|
DEFAULT_WALLED_GARDEN_URL);
|
|
mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
|
|
Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
|
|
DEFAULT_WALLED_GARDEN_INTERVAL_MS);
|
|
mShowDisabledNotification = getSettingsBoolean(mContentResolver,
|
|
Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true);
|
|
}
|
|
|
|
/**
|
|
* Helper to return wait time left given a min interval and last run
|
|
*
|
|
* @param interval minimum wait interval
|
|
* @param lastTime last time action was performed in
|
|
* SystemClock.elapsedRealtime(). Null if never.
|
|
* @return non negative time to wait
|
|
*/
|
|
private static long waitTime(long interval, Long lastTime) {
|
|
if (lastTime == null)
|
|
return 0;
|
|
long wait = interval + lastTime - SystemClock.elapsedRealtime();
|
|
return wait > 0 ? wait : 0;
|
|
}
|
|
|
|
private static String wifiInfoToStr(WifiInfo wifiInfo) {
|
|
if (wifiInfo == null)
|
|
return "null";
|
|
return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
|
|
}
|
|
|
|
/**
|
|
* Uses {@link #mConnectionInfo}.
|
|
*/
|
|
private void updateBssids() {
|
|
String curSsid = mConnectionInfo.getSSID();
|
|
List<ScanResult> results = mWifiManager.getScanResults();
|
|
int oldNumBssids = mBssids.size();
|
|
|
|
if (results == null) {
|
|
if (DBG) {
|
|
log("updateBssids: Got null scan results!");
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (ScanResult result : results) {
|
|
if (result == null || result.SSID == null) {
|
|
if (DBG) {
|
|
log("Received invalid scan result: " + result);
|
|
}
|
|
continue;
|
|
}
|
|
if (curSsid.equals(result.SSID))
|
|
mBssids.add(result.BSSID);
|
|
}
|
|
}
|
|
|
|
private void resetWatchdogState() {
|
|
if (DBG) {
|
|
log("Resetting watchdog state...");
|
|
}
|
|
mConnectionInfo = null;
|
|
mDisableAPNextFailure = false;
|
|
mLastWalledGardenCheckTime = null;
|
|
mNumCheckFailures = 0;
|
|
mBssids.clear();
|
|
setDisabledNetworkNotificationVisible(false);
|
|
setWalledGardenNotificationVisible(false);
|
|
}
|
|
|
|
private void setWalledGardenNotificationVisible(boolean visible) {
|
|
// If it should be hidden and it is already hidden, then noop
|
|
if (!visible && !mWalledGardenNotificationShown) {
|
|
return;
|
|
}
|
|
|
|
Resources r = Resources.getSystem();
|
|
NotificationManager notificationManager = (NotificationManager) mContext
|
|
.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
if (visible) {
|
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mWalledGardenUrl));
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
|
|
CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
|
|
mConnectionInfo.getSSID());
|
|
|
|
Notification notification = new Notification();
|
|
notification.when = 0;
|
|
notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range;
|
|
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
|
notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
|
|
notification.tickerText = title;
|
|
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
|
|
|
|
notificationManager.notify(WALLED_GARDEN_NOTIFICATION_ID, 1, notification);
|
|
} else {
|
|
notificationManager.cancel(WALLED_GARDEN_NOTIFICATION_ID, 1);
|
|
}
|
|
mWalledGardenNotificationShown = visible;
|
|
}
|
|
|
|
private void setDisabledNetworkNotificationVisible(boolean visible) {
|
|
// If it should be hidden and it is already hidden, then noop
|
|
if (!visible && !mDisabledNotificationShown) {
|
|
return;
|
|
}
|
|
|
|
Resources r = Resources.getSystem();
|
|
NotificationManager notificationManager = (NotificationManager) mContext
|
|
.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
if (visible) {
|
|
CharSequence title = r.getText(R.string.wifi_watchdog_network_disabled);
|
|
String msg = mConnectionInfo.getSSID() +
|
|
r.getText(R.string.wifi_watchdog_network_disabled_detailed);
|
|
|
|
Notification wifiDisabledWarning = new Notification.Builder(mContext)
|
|
.setSmallIcon(R.drawable.stat_sys_warning)
|
|
.setDefaults(Notification.DEFAULT_ALL)
|
|
.setTicker(title)
|
|
.setContentTitle(title)
|
|
.setContentText(msg)
|
|
.setContentIntent(PendingIntent.getActivity(mContext, 0,
|
|
new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
|
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
|
|
.setWhen(System.currentTimeMillis())
|
|
.setAutoCancel(true)
|
|
.getNotification();
|
|
|
|
notificationManager.notify(DISABLED_NETWORK_NOTIFICATION_ID, 1, wifiDisabledWarning);
|
|
} else {
|
|
notificationManager.cancel(DISABLED_NETWORK_NOTIFICATION_ID, 1);
|
|
}
|
|
mDisabledNotificationShown = visible;
|
|
}
|
|
|
|
class DefaultState extends State {
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_WATCHDOG_SETTINGS_CHANGE:
|
|
updateSettings();
|
|
if (DBG) {
|
|
log("Updating wifi-watchdog secure settings");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
if (DBG) {
|
|
log("Caught message " + msg.what + " in state " +
|
|
getCurrentState().getName());
|
|
}
|
|
return HANDLED;
|
|
}
|
|
}
|
|
|
|
class WatchdogDisabledState extends State {
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_WATCHDOG_TOGGLED:
|
|
if (isWatchdogEnabled())
|
|
transitionTo(mNotConnectedState);
|
|
return HANDLED;
|
|
}
|
|
return NOT_HANDLED;
|
|
}
|
|
}
|
|
|
|
class WatchdogEnabledState extends State {
|
|
@Override
|
|
public void enter() {
|
|
resetWatchdogState();
|
|
mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
|
|
if (DBG) log("WifiWatchdogService enabled");
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_WATCHDOG_TOGGLED:
|
|
if (!isWatchdogEnabled())
|
|
transitionTo(mWatchdogDisabledState);
|
|
return HANDLED;
|
|
case EVENT_NETWORK_STATE_CHANGE:
|
|
Intent stateChangeIntent = (Intent) msg.obj;
|
|
NetworkInfo networkInfo = (NetworkInfo)
|
|
stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
|
|
|
|
setDisabledNetworkNotificationVisible(false);
|
|
setWalledGardenNotificationVisible(false);
|
|
switch (networkInfo.getState()) {
|
|
case CONNECTED:
|
|
WifiInfo wifiInfo = (WifiInfo)
|
|
stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
|
|
if (wifiInfo == null) {
|
|
loge("Connected --> WifiInfo object null!");
|
|
return HANDLED;
|
|
}
|
|
|
|
if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
|
|
loge("Received wifiInfo object with null elts: "
|
|
+ wifiInfoToStr(wifiInfo));
|
|
return HANDLED;
|
|
}
|
|
|
|
initConnection(wifiInfo);
|
|
mConnectionInfo = wifiInfo;
|
|
mNetEventCounter++;
|
|
if (mPoorNetworkDetectionEnabled) {
|
|
updateBssids();
|
|
transitionTo(mDnsCheckingState);
|
|
} else {
|
|
transitionTo(mDelayWalledGardenState);
|
|
}
|
|
break;
|
|
default:
|
|
mNetEventCounter++;
|
|
transitionTo(mNotConnectedState);
|
|
break;
|
|
}
|
|
return HANDLED;
|
|
case EVENT_WIFI_RADIO_STATE_CHANGE:
|
|
if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
|
|
if (DBG) log("WifiStateDisabling -- Resetting WatchdogState");
|
|
resetWatchdogState();
|
|
mNetEventCounter++;
|
|
transitionTo(mNotConnectedState);
|
|
}
|
|
return HANDLED;
|
|
}
|
|
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
/**
|
|
* @param wifiInfo Info object with non-null ssid and bssid
|
|
*/
|
|
private void initConnection(WifiInfo wifiInfo) {
|
|
if (DBG) {
|
|
log("Connected:: old " + wifiInfoToStr(mConnectionInfo) +
|
|
" ==> new " + wifiInfoToStr(wifiInfo));
|
|
}
|
|
|
|
if (mConnectionInfo == null || !wifiInfo.getSSID().equals(mConnectionInfo.getSSID())) {
|
|
resetWatchdogState();
|
|
} else if (!wifiInfo.getBSSID().equals(mConnectionInfo.getBSSID())) {
|
|
mDisableAPNextFailure = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void exit() {
|
|
mContext.unregisterReceiver(mBroadcastReceiver);
|
|
if (DBG) log("WifiWatchdogService disabled");
|
|
}
|
|
}
|
|
|
|
class NotConnectedState extends State {
|
|
}
|
|
|
|
class ConnectedState extends State {
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_SCAN_RESULTS_AVAILABLE:
|
|
if (mPoorNetworkDetectionEnabled) {
|
|
updateBssids();
|
|
}
|
|
return HANDLED;
|
|
case EVENT_WATCHDOG_SETTINGS_CHANGE:
|
|
updateSettings();
|
|
if (mPoorNetworkDetectionEnabled) {
|
|
transitionTo(mOnlineWatchState);
|
|
} else {
|
|
transitionTo(mOnlineState);
|
|
}
|
|
return HANDLED;
|
|
}
|
|
return NOT_HANDLED;
|
|
}
|
|
}
|
|
|
|
class DnsCheckingState extends State {
|
|
List<InetAddress> mDnsList;
|
|
int[] dnsCheckSuccesses;
|
|
String dnsCheckLogStr;
|
|
String[] dnsResponseStrs;
|
|
/** Keeps track of active dns pings. Map is from pingID to index in mDnsList */
|
|
HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
|
|
|
|
@Override
|
|
public void enter() {
|
|
mDnsList = mDnsPinger.getDnsList();
|
|
int numDnses = mDnsList.size();
|
|
dnsCheckSuccesses = new int[numDnses];
|
|
dnsResponseStrs = new String[numDnses];
|
|
for (int i = 0; i < numDnses; i++)
|
|
dnsResponseStrs[i] = "";
|
|
|
|
if (DBG) {
|
|
dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
|
|
mDnsList, mConnectionInfo.getSSID());
|
|
log(dnsCheckLogStr);
|
|
}
|
|
|
|
idDnsMap.clear();
|
|
for (int i=0; i < mNumDnsPings; i++) {
|
|
for (int j = 0; j < numDnses; j++) {
|
|
idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
|
|
DNS_START_DELAY_MS + DNS_INTRATEST_PING_INTERVAL_MS * i), j);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
if (msg.what != DnsPinger.DNS_PING_RESULT) {
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
int pingID = msg.arg1;
|
|
int pingResponseTime = msg.arg2;
|
|
|
|
Integer dnsServerId = idDnsMap.get(pingID);
|
|
if (dnsServerId == null) {
|
|
loge("Received a Dns response with unknown ID!");
|
|
return HANDLED;
|
|
}
|
|
|
|
idDnsMap.remove(pingID);
|
|
if (pingResponseTime >= 0)
|
|
dnsCheckSuccesses[dnsServerId]++;
|
|
|
|
if (DBG) {
|
|
if (pingResponseTime >= 0) {
|
|
dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
|
|
} else {
|
|
dnsResponseStrs[dnsServerId] += "|x";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* After a full ping count, if we have more responses than this
|
|
* cutoff, the outcome is success; else it is 'failure'.
|
|
*/
|
|
|
|
/**
|
|
* Our final success count will be at least this big, so we're
|
|
* guaranteed to succeed.
|
|
*/
|
|
if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
|
|
// DNS CHECKS OK, NOW WALLED GARDEN
|
|
if (DBG) {
|
|
log(makeLogString() + " SUCCESS");
|
|
}
|
|
|
|
if (!shouldCheckWalledGarden()) {
|
|
transitionTo(mOnlineWatchState);
|
|
return HANDLED;
|
|
}
|
|
|
|
transitionTo(mDelayWalledGardenState);
|
|
return HANDLED;
|
|
}
|
|
|
|
if (idDnsMap.isEmpty()) {
|
|
if (DBG) {
|
|
log(makeLogString() + " FAILURE");
|
|
}
|
|
transitionTo(mDnsCheckFailureState);
|
|
return HANDLED;
|
|
}
|
|
|
|
return HANDLED;
|
|
}
|
|
|
|
private String makeLogString() {
|
|
String logStr = dnsCheckLogStr;
|
|
for (String respStr : dnsResponseStrs)
|
|
logStr += " [" + respStr + "]";
|
|
return logStr;
|
|
}
|
|
|
|
@Override
|
|
public void exit() {
|
|
mDnsPinger.cancelPings();
|
|
}
|
|
|
|
private boolean shouldCheckWalledGarden() {
|
|
if (!mWalledGardenTestEnabled) {
|
|
if (DBG)
|
|
log("Skipping walled garden check - disabled");
|
|
return false;
|
|
}
|
|
long waitTime = waitTime(mWalledGardenIntervalMs,
|
|
mLastWalledGardenCheckTime);
|
|
if (waitTime > 0) {
|
|
if (DBG) {
|
|
log("Skipping walled garden check - wait " +
|
|
waitTime + " ms.");
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class DelayWalledGardenState extends State {
|
|
@Override
|
|
public void enter() {
|
|
sendMessageDelayed(MESSAGE_DELAYED_WALLED_GARDEN_CHECK, WALLED_GARDEN_START_DELAY_MS);
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case MESSAGE_DELAYED_WALLED_GARDEN_CHECK:
|
|
mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
|
|
if (isWalledGardenConnection()) {
|
|
if (DBG) log("Walled garden test complete - walled garden detected");
|
|
transitionTo(mWalledGardenState);
|
|
} else {
|
|
if (DBG) log("Walled garden test complete - online");
|
|
if (mPoorNetworkDetectionEnabled) {
|
|
transitionTo(mOnlineWatchState);
|
|
} else {
|
|
transitionTo(mOnlineState);
|
|
}
|
|
}
|
|
return HANDLED;
|
|
default:
|
|
return NOT_HANDLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
class OnlineWatchState extends State {
|
|
/**
|
|
* Signals a short-wait message is enqueued for the current 'guard' counter
|
|
*/
|
|
boolean unstableSignalChecks = false;
|
|
|
|
/**
|
|
* The signal is unstable. We should enqueue a short-wait check, if one is enqueued
|
|
* already
|
|
*/
|
|
boolean signalUnstable = false;
|
|
|
|
/**
|
|
* A monotonic counter to ensure that at most one check message will be processed from any
|
|
* set of check messages currently enqueued. Avoids duplicate checks when a low-signal
|
|
* event is observed.
|
|
*/
|
|
int checkGuard = 0;
|
|
Long lastCheckTime = null;
|
|
|
|
/** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */
|
|
HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
|
|
|
|
@Override
|
|
public void enter() {
|
|
lastCheckTime = SystemClock.elapsedRealtime();
|
|
signalUnstable = false;
|
|
checkGuard++;
|
|
unstableSignalChecks = false;
|
|
pingInfoMap.clear();
|
|
triggerSingleDnsCheck();
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case EVENT_RSSI_CHANGE:
|
|
if (msg.arg1 != mNetEventCounter) {
|
|
if (DBG) {
|
|
log("Rssi change message out of sync, ignoring");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
int newRssi = msg.arg2;
|
|
signalUnstable = !rssiStrengthAboveCutoff(newRssi);
|
|
if (DBG) {
|
|
log("OnlineWatchState:: new rssi " + newRssi + " --> level " +
|
|
WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
|
|
}
|
|
|
|
if (signalUnstable && !unstableSignalChecks) {
|
|
if (DBG) {
|
|
log("Sending triggered check msg");
|
|
}
|
|
triggerSingleDnsCheck();
|
|
}
|
|
return HANDLED;
|
|
case MESSAGE_SINGLE_DNS_CHECK:
|
|
if (msg.arg1 != checkGuard) {
|
|
if (DBG) {
|
|
log("Single check msg out of sync, ignoring.");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
lastCheckTime = SystemClock.elapsedRealtime();
|
|
pingInfoMap.clear();
|
|
for (InetAddress curDns: mDnsPinger.getDnsList()) {
|
|
pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
|
|
curDns);
|
|
}
|
|
return HANDLED;
|
|
case DnsPinger.DNS_PING_RESULT:
|
|
InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
|
|
if (curDnsServer == null) {
|
|
return HANDLED;
|
|
}
|
|
pingInfoMap.remove(msg.arg1);
|
|
int responseTime = msg.arg2;
|
|
if (responseTime >= 0) {
|
|
if (DBG) {
|
|
log("Single DNS ping OK. Response time: "
|
|
+ responseTime + " from DNS " + curDnsServer);
|
|
}
|
|
pingInfoMap.clear();
|
|
|
|
checkGuard++;
|
|
unstableSignalChecks = false;
|
|
triggerSingleDnsCheck();
|
|
} else {
|
|
if (pingInfoMap.isEmpty()) {
|
|
if (DBG) {
|
|
log("Single dns ping failure. All dns servers failed, "
|
|
+ "starting full checks.");
|
|
}
|
|
transitionTo(mDnsCheckingState);
|
|
}
|
|
}
|
|
return HANDLED;
|
|
}
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
@Override
|
|
public void exit() {
|
|
mDnsPinger.cancelPings();
|
|
}
|
|
|
|
/**
|
|
* Times a dns check with an interval based on {@link #signalUnstable}
|
|
*/
|
|
private void triggerSingleDnsCheck() {
|
|
long waitInterval;
|
|
if (signalUnstable) {
|
|
waitInterval = mDnsCheckShortIntervalMs;
|
|
unstableSignalChecks = true;
|
|
} else {
|
|
waitInterval = mDnsCheckLongIntervalMs;
|
|
}
|
|
sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
|
|
waitTime(waitInterval, lastCheckTime));
|
|
}
|
|
}
|
|
|
|
|
|
/* Child state of ConnectedState indicating that we are online
|
|
* and there is nothing to do
|
|
*/
|
|
class OnlineState extends State {
|
|
}
|
|
|
|
class DnsCheckFailureState extends State {
|
|
|
|
@Override
|
|
public void enter() {
|
|
mNumCheckFailures++;
|
|
obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
if (msg.what != MESSAGE_HANDLE_BAD_AP) {
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
if (msg.arg1 != mNetEventCounter) {
|
|
if (DBG) {
|
|
log("Msg out of sync, ignoring...");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
|
|
if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
|
|
|| mNumCheckFailures >= mMaxSsidBlacklists) {
|
|
if (sWifiOnly) {
|
|
log("Would disable bad network, but device has no mobile data!" +
|
|
" Going idle...");
|
|
// This state should be called idle -- will be changing flow.
|
|
transitionTo(mNotConnectedState);
|
|
return HANDLED;
|
|
}
|
|
|
|
// TODO : Unban networks if they had low signal ?
|
|
log("Disabling current SSID " + wifiInfoToStr(mConnectionInfo)
|
|
+ ". " + "numCheckFailures " + mNumCheckFailures
|
|
+ ", numAPs " + mBssids.size());
|
|
int networkId = mConnectionInfo.getNetworkId();
|
|
if (!mHasConnectedWifiManager) {
|
|
mWifiManager.asyncConnect(mContext, getHandler());
|
|
mHasConnectedWifiManager = true;
|
|
}
|
|
mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE);
|
|
if (mShowDisabledNotification && mConnectionInfo.isExplicitConnect()) {
|
|
setDisabledNetworkNotificationVisible(true);
|
|
}
|
|
transitionTo(mNotConnectedState);
|
|
} else {
|
|
log("Blacklisting current BSSID. " + wifiInfoToStr(mConnectionInfo)
|
|
+ "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size());
|
|
|
|
mWifiManager.addToBlacklist(mConnectionInfo.getBSSID());
|
|
mWifiManager.reassociate();
|
|
transitionTo(mBlacklistedApState);
|
|
}
|
|
return HANDLED;
|
|
}
|
|
}
|
|
|
|
class WalledGardenState extends State {
|
|
@Override
|
|
public void enter() {
|
|
obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
if (msg.arg1 != mNetEventCounter) {
|
|
if (DBG) {
|
|
log("WalledGardenState::Msg out of sync, ignoring...");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
setWalledGardenNotificationVisible(true);
|
|
if (mPoorNetworkDetectionEnabled) {
|
|
transitionTo(mOnlineWatchState);
|
|
} else {
|
|
transitionTo(mOnlineState);
|
|
}
|
|
return HANDLED;
|
|
}
|
|
}
|
|
|
|
class BlacklistedApState extends State {
|
|
@Override
|
|
public void enter() {
|
|
mDisableAPNextFailure = true;
|
|
sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
|
|
mBlacklistFollowupIntervalMs);
|
|
}
|
|
|
|
@Override
|
|
public boolean processMessage(Message msg) {
|
|
if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
|
|
return NOT_HANDLED;
|
|
}
|
|
|
|
if (msg.arg1 != mNetEventCounter) {
|
|
if (DBG) {
|
|
log("BlacklistedApState::Msg out of sync, ignoring...");
|
|
}
|
|
return HANDLED;
|
|
}
|
|
|
|
transitionTo(mDnsCheckingState);
|
|
return HANDLED;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Convenience function for retrieving a single secure settings value
|
|
* as a string with a default value.
|
|
*
|
|
* @param cr The ContentResolver to access.
|
|
* @param name The name of the setting to retrieve.
|
|
* @param def Value to return if the setting is not defined.
|
|
*
|
|
* @return The setting's current value, or 'def' if it is not defined
|
|
*/
|
|
private static String getSettingsStr(ContentResolver cr, String name, String def) {
|
|
String v = Settings.Secure.getString(cr, name);
|
|
return v != null ? v : def;
|
|
}
|
|
|
|
/**
|
|
* Convenience function for retrieving a single secure settings value
|
|
* as a boolean. Note that internally setting values are always
|
|
* stored as strings; this function converts the string to a boolean
|
|
* for you. The default value will be returned if the setting is
|
|
* not defined or not a valid boolean.
|
|
*
|
|
* @param cr The ContentResolver to access.
|
|
* @param name The name of the setting to retrieve.
|
|
* @param def Value to return if the setting is not defined.
|
|
*
|
|
* @return The setting's current value, or 'def' if it is not defined
|
|
* or not a valid boolean.
|
|
*/
|
|
private static boolean getSettingsBoolean(ContentResolver cr, String name, boolean def) {
|
|
return Settings.Secure.getInt(cr, name, def ? 1 : 0) == 1;
|
|
}
|
|
|
|
/**
|
|
* Convenience function for updating a single settings value as an
|
|
* integer. This will either create a new entry in the table if the
|
|
* given name does not exist, or modify the value of the existing row
|
|
* with that name. Note that internally setting values are always
|
|
* stored as strings, so this function converts the given value to a
|
|
* string before storing it.
|
|
*
|
|
* @param cr The ContentResolver to access.
|
|
* @param name The name of the setting to modify.
|
|
* @param value The new value for the setting.
|
|
* @return true if the value was set, false on database errors
|
|
*/
|
|
private static boolean putSettingsBoolean(ContentResolver cr, String name, boolean value) {
|
|
return Settings.Secure.putInt(cr, name, value ? 1 : 0);
|
|
}
|
|
|
|
private void log(String s) {
|
|
Log.d(TAG, s);
|
|
}
|
|
|
|
private void loge(String s) {
|
|
Log.e(TAG, s);
|
|
}
|
|
}
|