Files
frameworks_base/wifi/java/android/net/wifi/WifiStateTracker.java
Irfan Sheriff 22c2b25ad7 sticky broadcasts dont need system ready check
Fix an issue where checking for system ready status
for sticky broadcasts can lead to boot up with wifi
turned on and settings showing incorrect status.

Bug: 2876746
Change-Id: Iae39855c255513e28a13bd819b3d14d81fbaba43
2010-07-28 11:19:54 -07:00

3968 lines
151 KiB
Java

/*
* Copyright (C) 2010 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 static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
/**
* TODO: Add soft AP states as part of WIFI_STATE_XXX
* Retain WIFI_STATE_ENABLING that indicates driver is loading
* Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
* and WIFI_STATE_FAILED for failure
* Deprecate WIFI_STATE_UNKNOWN
*
* Doing this will simplify the logic for sending broadcasts
*/
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
import android.app.ActivityManagerNative;
import android.net.NetworkInfo;
import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
import android.net.NetworkUtils;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkProperties;
import android.os.Binder;
import android.os.Message;
import android.os.Parcelable;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Process;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.backup.IBackupManager;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothA2dp;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.Context;
import android.database.ContentObserver;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.HierarchicalState;
import com.android.internal.util.HierarchicalStateMachine;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
/**
* Track the state of Wifi connectivity. All event handling is done here,
* and all changes in connectivity state are initiated here.
*
* @hide
*/
//TODO: we still need frequent scanning for the case when
// we issue disconnect but need scan results for open network notification
public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker {
private static final String TAG = "WifiStateTracker";
private static final String NETWORKTYPE = "WIFI";
private static final boolean DBG = false;
/* TODO: fetch a configurable interface */
private static final String SOFTAP_IFACE = "wl0.1";
private WifiMonitor mWifiMonitor;
private INetworkManagementService nwService;
private ConnectivityManager mCm;
/* Scan results handling */
private List<ScanResult> mScanResults;
private static final Pattern scanResultPattern = Pattern.compile("\t+");
private static final int SCAN_RESULT_CACHE_SIZE = 80;
private final LinkedHashMap<String, ScanResult> mScanResultCache;
private String mInterfaceName;
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
private int mNumAllowedChannels = 0;
private int mLastSignalLevel = -1;
private String mLastBssid;
private int mLastNetworkId;
private boolean mEnableRssiPolling = false;
private boolean mPasswordKeyMayBeIncorrect = false;
private boolean mUseStaticIp = false;
private int mReconnectCount = 0;
private boolean mIsScanMode = false;
/**
* Instance of the bluetooth headset helper. This needs to be created
* early because there is a delay before it actually 'connects', as
* noted by its javadoc. If we check before it is connected, it will be
* in an error state and we will not disable coexistence.
*/
private BluetoothHeadset mBluetoothHeadset;
private BluetoothA2dp mBluetoothA2dp;
/**
* Observes the static IP address settings.
*/
private SettingsObserver mSettingsObserver;
private NetworkProperties mNetworkProperties;
// Variables relating to the 'available networks' notification
/**
* The icon to show in the 'available networks' notification. This will also
* be the ID of the Notification given to the NotificationManager.
*/
private static final int ICON_NETWORKS_AVAILABLE =
com.android.internal.R.drawable.stat_notify_wifi_in_range;
/**
* When a notification is shown, we wait this amount before possibly showing it again.
*/
private final long NOTIFICATION_REPEAT_DELAY_MS;
/**
* Whether the user has set the setting to show the 'available networks' notification.
*/
private boolean mNotificationEnabled;
/**
* Observes the user setting to keep {@link #mNotificationEnabled} in sync.
*/
private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
/**
* The {@link System#currentTimeMillis()} must be at least this value for us
* to show the notification again.
*/
private long mNotificationRepeatTime;
/**
* The Notification object given to the NotificationManager.
*/
private Notification mNotification;
/**
* Whether the notification is being shown, as set by us. That is, if the
* user cancels the notification, we will not receive the callback so this
* will still be true. We only guarantee if this is false, then the
* notification is not showing.
*/
private boolean mNotificationShown;
/**
* The number of continuous scans that must occur before consider the
* supplicant in a scanning state. This allows supplicant to associate with
* remembered networks that are in the scan results.
*/
private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
/**
* The number of scans since the last network state change. When this
* exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
* supplicant to actually be scanning. When the network state changes to
* something other than scanning, we reset this to 0.
*/
private int mNumScansSinceNetworkStateChange;
// Held during driver load and unload
private static PowerManager.WakeLock sWakeLock;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
private DhcpInfo mDhcpInfo;
private WifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private SupplicantStateTracker mSupplicantStateTracker;
// Event log tags (must be in sync with event-log-tags)
private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
/* Load the driver */
private static final int CMD_LOAD_DRIVER = 1;
/* Unload the driver */
private static final int CMD_UNLOAD_DRIVER = 2;
/* Indicates driver load succeeded */
private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
/* Indicates driver load failed */
private static final int CMD_LOAD_DRIVER_FAILURE = 4;
/* Indicates driver unload succeeded */
private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
/* Indicates driver unload failed */
private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
/* Start the supplicant */
private static final int CMD_START_SUPPLICANT = 11;
/* Stop the supplicant */
private static final int CMD_STOP_SUPPLICANT = 12;
/* Start the driver */
private static final int CMD_START_DRIVER = 13;
/* Start the driver */
private static final int CMD_STOP_DRIVER = 14;
/* Indicates DHCP succeded */
private static final int CMD_IP_CONFIG_SUCCESS = 15;
/* Indicates DHCP failed */
private static final int CMD_IP_CONFIG_FAILURE = 16;
/* Re-configure interface */
private static final int CMD_RECONFIGURE_IP = 17;
/* Start the soft access point */
private static final int CMD_START_AP = 21;
/* Stop the soft access point */
private static final int CMD_STOP_AP = 22;
/* Supplicant events */
/* Connection to supplicant established */
private static final int SUP_CONNECTION_EVENT = 31;
/* Connection to supplicant lost */
private static final int SUP_DISCONNECTION_EVENT = 32;
/* Driver start completed */
private static final int DRIVER_START_EVENT = 33;
/* Driver stop completed */
private static final int DRIVER_STOP_EVENT = 34;
/* Network connection completed */
private static final int NETWORK_CONNECTION_EVENT = 36;
/* Network disconnection completed */
private static final int NETWORK_DISCONNECTION_EVENT = 37;
/* Scan results are available */
private static final int SCAN_RESULTS_EVENT = 38;
/* Supplicate state changed */
private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
/* Password may be incorrect */
private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
/* Supplicant commands */
/* Is supplicant alive ? */
private static final int CMD_PING_SUPPLICANT = 51;
/* Add/update a network configuration */
private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
/* Delete a network */
private static final int CMD_REMOVE_NETWORK = 53;
/* Enable a network. The device will attempt a connection to the given network. */
private static final int CMD_ENABLE_NETWORK = 54;
/* Disable a network. The device does not attempt a connection to the given network. */
private static final int CMD_DISABLE_NETWORK = 55;
/* Blacklist network. De-prioritizes the given BSSID for connection. */
private static final int CMD_BLACKLIST_NETWORK = 56;
/* Clear the blacklist network list */
private static final int CMD_CLEAR_BLACKLIST = 57;
/* Get the configured networks */
private static final int CMD_GET_NETWORK_CONFIG = 58;
/* Save configuration */
private static final int CMD_SAVE_CONFIG = 59;
/* Connection status */
private static final int CMD_CONNECTION_STATUS = 60;
/* Supplicant commands after driver start*/
/* Initiate a scan */
private static final int CMD_START_SCAN = 71;
/* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
private static final int CMD_SET_SCAN_MODE = 72;
/* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
private static final int CMD_SET_SCAN_TYPE = 73;
/* Disconnect from a network */
private static final int CMD_DISCONNECT = 74;
/* Reconnect to a network */
private static final int CMD_RECONNECT = 75;
/* Reassociate to a network */
private static final int CMD_REASSOCIATE = 76;
/* Set power mode
* POWER_MODE_ACTIVE
* POWER_MODE_AUTO
*/
private static final int CMD_SET_POWER_MODE = 77;
/* Set bluetooth co-existence
* BLUETOOTH_COEXISTENCE_MODE_ENABLED
* BLUETOOTH_COEXISTENCE_MODE_DISABLED
* BLUETOOTH_COEXISTENCE_MODE_SENSE
*/
private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
/* Enable/disable bluetooth scan mode
* true(1)
* false(0)
*/
private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
/* Set number of allowed channels */
private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
/* Request connectivity manager wake lock before driver stop */
private static final int CMD_REQUEST_CM_WAKELOCK = 81;
/* Enables RSSI poll */
private static final int CMD_ENABLE_RSSI_POLL = 82;
/* RSSI poll */
private static final int CMD_RSSI_POLL = 83;
/* Get current RSSI */
private static final int CMD_GET_RSSI = 84;
/* Get approx current RSSI */
private static final int CMD_GET_RSSI_APPROX = 85;
/* Get link speed on connection */
private static final int CMD_GET_LINK_SPEED = 86;
/* Radio mac address */
private static final int CMD_GET_MAC_ADDR = 87;
/* Set up packet filtering */
private static final int CMD_START_PACKET_FILTERING = 88;
/* Clear packet filter */
private static final int CMD_STOP_PACKET_FILTERING = 89;
/* Connectivity service commands */
/* Bring down wifi connection */
private static final int CM_CMD_TEARDOWN = 110;
/* Reconnect to wifi */
private static final int CM_CMD_RECONNECT = 111;
/**
* Interval in milliseconds between polling for connection
* status items that are not sent via asynchronous events.
* An example is RSSI (signal strength).
*/
private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
private static final int CONNECT_MODE = 1;
private static final int SCAN_ONLY_MODE = 2;
private static final int SCAN_ACTIVE = 1;
private static final int SCAN_PASSIVE = 2;
/**
* The maximum number of times we will retry a connection to an access point
* for which we have failed in acquiring an IP address from DHCP. A value of
* N means that we will make N+1 connection attempts in all.
* <p>
* See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
* value if a Settings value is not present.
*/
private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
private static final int DRIVER_POWER_MODE_ACTIVE = 1;
private static final int DRIVER_POWER_MODE_AUTO = 0;
/* Default parent state */
private HierarchicalState mDefaultState = new DefaultState();
/* Temporary initial state */
private HierarchicalState mInitialState = new InitialState();
/* Unloading the driver */
private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
/* Loading the driver */
private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
/* Driver load/unload failed */
private HierarchicalState mDriverFailedState = new DriverFailedState();
/* Driver loading */
private HierarchicalState mDriverLoadingState = new DriverLoadingState();
/* Driver loaded */
private HierarchicalState mDriverLoadedState = new DriverLoadedState();
/* Driver loaded, waiting for supplicant to start */
private HierarchicalState mWaitForSupState = new WaitForSupState();
/* Driver loaded and supplicant ready */
private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
/* Driver start issued, waiting for completed event */
private HierarchicalState mDriverStartingState = new DriverStartingState();
/* Driver started */
private HierarchicalState mDriverStartedState = new DriverStartedState();
/* Driver stopping */
private HierarchicalState mDriverStoppingState = new DriverStoppingState();
/* Driver stopped */
private HierarchicalState mDriverStoppedState = new DriverStoppedState();
/* Scan for networks, no connection will be established */
private HierarchicalState mScanModeState = new ScanModeState();
/* Connecting to an access point */
private HierarchicalState mConnectModeState = new ConnectModeState();
/* Fetching IP after network connection (assoc+auth complete) */
private HierarchicalState mConnectingState = new ConnectingState();
/* Connected with IP addr */
private HierarchicalState mConnectedState = new ConnectedState();
/* disconnect issued, waiting for network disconnect confirmation */
private HierarchicalState mDisconnectingState = new DisconnectingState();
/* Network is not connected, supplicant assoc+auth is not complete */
private HierarchicalState mDisconnectedState = new DisconnectedState();
/* Soft Ap is running */
private HierarchicalState mSoftApStartedState = new SoftApStartedState();
/* Argument for Message object to indicate a synchronous call */
private static final int SYNCHRONOUS_CALL = 1;
private static final int ASYNCHRONOUS_CALL = 0;
/**
* One of {@link WifiManager#WIFI_STATE_DISABLED},
* {@link WifiManager#WIFI_STATE_DISABLING},
* {@link WifiManager#WIFI_STATE_ENABLED},
* {@link WifiManager#WIFI_STATE_ENABLING},
* {@link WifiManager#WIFI_STATE_UNKNOWN}
*
*/
private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
/**
* One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
* {@link WifiManager#WIFI_AP_STATE_DISABLING},
* {@link WifiManager#WIFI_AP_STATE_ENABLED},
* {@link WifiManager#WIFI_AP_STATE_ENABLING},
* {@link WifiManager#WIFI_AP_STATE_FAILED}
*
*/
private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
private final IBatteryStats mBatteryStats;
public WifiStateTracker(Context context, Handler target) {
super(NETWORKTYPE);
mCsHandler = target;
mContext = context;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
nwService = INetworkManagementService.Stub.asInterface(b);
mWifiMonitor = new WifiMonitor(this);
mDhcpInfo = new DhcpInfo();
mWifiInfo = new WifiInfo();
mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
mBluetoothHeadset = new BluetoothHeadset(mContext, null);
mNetworkProperties = new NetworkProperties();
mNetworkInfo.setIsAvailable(false);
mNetworkProperties.clear();
resetNotificationTimer();
setTeardownRequested(false);
mLastBssid = null;
mLastNetworkId = -1;
mLastSignalLevel = -1;
mScanResultCache = new LinkedHashMap<String, ScanResult>(
SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
/*
* Limit the cache size by SCAN_RESULT_CACHE_SIZE
* elements
*/
@Override
public boolean removeEldestEntry(Map.Entry eldest) {
return SCAN_RESULT_CACHE_SIZE < this.size();
}
};
// Setting is in seconds
NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target);
mNotificationEnabledSettingObserver.register();
mSettingsObserver = new SettingsObserver(new Handler());
PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mDriverUnloadingState, mDefaultState);
addState(mDriverUnloadedState, mDefaultState);
addState(mDriverFailedState, mDriverUnloadedState);
addState(mDriverLoadingState, mDefaultState);
addState(mDriverLoadedState, mDefaultState);
addState(mWaitForSupState, mDriverLoadedState);
addState(mDriverSupReadyState, mDefaultState);
addState(mDriverStartingState, mDriverSupReadyState);
addState(mDriverStartedState, mDriverSupReadyState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
addState(mConnectingState, mConnectModeState);
addState(mConnectedState, mConnectModeState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mDriverStoppingState, mDriverSupReadyState);
addState(mDriverStoppedState, mDriverSupReadyState);
addState(mSoftApStartedState, mDefaultState);
setInitialState(mInitialState);
if (DBG) setDbg(true);
//start the state machine
start();
}
/*********************************************************
* NetworkStateTracker interface implementation
********************************************************/
public void setTeardownRequested(boolean isRequested) {
mTeardownRequested.set(isRequested);
}
public boolean isTeardownRequested() {
return mTeardownRequested.get();
}
/**
* Begin monitoring wifi connectivity
* @deprecated
*
* TODO: remove this from callers
*/
public void startMonitoring() {
}
/**
* Disable connectivity to a network
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean teardown() {
sendMessage(CM_CMD_TEARDOWN);
return true;
}
/**
* Re-enable connectivity to a network after a {@link #teardown()}.
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean reconnect() {
sendMessage(CM_CMD_RECONNECT);
return true;
}
/**
* Turn the wireless radio off for a network.
* @param turnOn {@code true} to turn the radio on, {@code false}
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean setRadio(boolean turnOn) {
setWifiEnabled(turnOn);
return true;
}
/**
* Wi-Fi is considered available as long as we have a connection to the
* supplicant daemon and there is at least one enabled network. If a teardown
* was explicitly requested, then Wi-Fi can be restarted with a reconnect
* request, so it is considered available. If the driver has been stopped
* for any reason other than a teardown request, Wi-Fi is considered
* unavailable.
* @return {@code true} if Wi-Fi connections are possible
*/
public boolean isAvailable() {
return mNetworkInfo.isAvailable();
}
/**
* Tells the underlying networking system that the caller wants to
* begin using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
* @param feature the name of the feature to be used
* @param callingPid the process ID of the process that is issuing this request
* @param callingUid the user ID of the process that is issuing this request
* @return an integer value representing the outcome of the request.
* The interpretation of this value is specific to each networking
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
* TODO: needs to go away
*/
public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
return -1;
}
/**
* Tells the underlying networking system that the caller is finished
* using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
* @param feature the name of the feature that is no longer needed.
* @param callingPid the process ID of the process that is issuing this request
* @param callingUid the user ID of the process that is issuing this request
* @return an integer value representing the outcome of the request.
* The interpretation of this value is specific to each networking
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
* TODO: needs to go away
*/
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
return -1;
}
/* TODO: will go away.
* Notifications are directly handled in WifiStateTracker at checkAndSetNotification()
*/
public void interpretScanResultsAvailable() {
}
/**
* Return the name of our WLAN network interface.
* @return the name of our interface.
*/
public String getInterfaceName() {
return mInterfaceName;
}
/**
* Check if private DNS route is set for the network
*/
public boolean isPrivateDnsRouteSet() {
return mPrivateDnsRouteSet.get();
}
/**
* Set a flag indicating private DNS route is set
*/
public void privateDnsRouteSet(boolean enabled) {
mPrivateDnsRouteSet.set(enabled);
}
/**
* Fetch NetworkInfo for the network
*/
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
/**
* Fetch NetworkProperties for the network
*/
public NetworkProperties getNetworkProperties() {
return mNetworkProperties;
}
/**
* Fetch default gateway address for the network
*/
public int getDefaultGatewayAddr() {
return mDefaultGatewayAddr.get();
}
/**
* Check if default route is set
*/
public boolean isDefaultRouteSet() {
return mDefaultRouteSet.get();
}
/**
* Set a flag indicating default route is set for the network
*/
public void defaultRouteSet(boolean enabled) {
mDefaultRouteSet.set(enabled);
}
/**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
*/
public String getTcpBufferSizesPropName() {
return "net.tcp.buffersize.wifi";
}
/*********************************************************
* Methods exposed for public use
********************************************************/
/**
* TODO: doc
*/
public boolean pingSupplicant() {
return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
}
/**
* TODO: doc
*/
public boolean startScan(boolean forceActive) {
return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
}
/**
* TODO: doc
*/
public void setWifiEnabled(boolean enable) {
mLastEnableUid.set(Binder.getCallingUid());
if (enable) {
/* Argument is the state that is entered prior to load */
sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
sendMessage(CMD_START_SUPPLICANT);
} else {
sendMessage(CMD_STOP_SUPPLICANT);
/* Argument is the state that is entered upon success */
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
}
}
/**
* TODO: doc
*/
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
mLastApEnableUid.set(Binder.getCallingUid());
if (enable) {
/* Argument is the state that is entered prior to load */
sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
} else {
sendMessage(CMD_STOP_AP);
/* Argument is the state that is entered upon success */
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
}
}
/**
* TODO: doc
*/
public int getWifiState() {
return mWifiState.get();
}
/**
* TODO: doc
*/
public String getWifiStateByName() {
switch (mWifiState.get()) {
case WIFI_STATE_DISABLING:
return "disabling";
case WIFI_STATE_DISABLED:
return "disabled";
case WIFI_STATE_ENABLING:
return "enabling";
case WIFI_STATE_ENABLED:
return "enabled";
case WIFI_STATE_UNKNOWN:
return "unknown state";
default:
return "[invalid state]";
}
}
/**
* TODO: doc
*/
public int getWifiApState() {
return mWifiApState.get();
}
/**
* TODO: doc
*/
public String getWifiApStateByName() {
switch (mWifiApState.get()) {
case WIFI_AP_STATE_DISABLING:
return "disabling";
case WIFI_AP_STATE_DISABLED:
return "disabled";
case WIFI_AP_STATE_ENABLING:
return "enabling";
case WIFI_AP_STATE_ENABLED:
return "enabled";
case WIFI_AP_STATE_FAILED:
return "failed";
default:
return "[invalid state]";
}
}
/**
* Get status information for the current connection, if any.
* @return a {@link WifiInfo} object containing information about the current connection
*
*/
public WifiInfo requestConnectionInfo() {
return mWifiInfo;
}
public DhcpInfo getDhcpInfo() {
return mDhcpInfo;
}
/**
* TODO: doc
*/
public void startWifi(boolean enable) {
if (enable) {
sendMessage(CMD_START_DRIVER);
} else {
sendMessage(CMD_STOP_DRIVER);
}
}
/**
* TODO: doc
*/
public void disconnectAndStop() {
sendMessage(CMD_DISCONNECT);
sendMessage(CMD_STOP_DRIVER);
}
/**
* TODO: doc
*/
public void setScanOnlyMode(boolean enable) {
if (enable) {
sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
} else {
sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
}
}
/**
* TODO: doc
*/
public void setScanType(boolean active) {
if (active) {
sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
} else {
sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
}
}
/**
* TODO: doc
*/
public List<ScanResult> getScanResultsList() {
return mScanResults;
}
/**
* Disconnect from Access Point
*/
public boolean disconnectCommand() {
return sendSyncMessage(CMD_DISCONNECT).boolValue;
}
/**
* Initiate a reconnection to AP
*/
public boolean reconnectCommand() {
return sendSyncMessage(CMD_RECONNECT).boolValue;
}
/**
* Initiate a re-association to AP
*/
public boolean reassociateCommand() {
return sendSyncMessage(CMD_REASSOCIATE).boolValue;
}
/**
* Add a network synchronously
*
* @return network id of the new network
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
}
public List<WifiConfiguration> getConfiguredNetworks() {
return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
}
/**
* Delete a network
*
* @param networkId id of the network to be removed
*/
public boolean removeNetwork(int networkId) {
return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
}
private class EnableNetParams {
private int netId;
private boolean disableOthers;
EnableNetParams(int n, boolean b) {
netId = n;
disableOthers = b;
}
}
/**
* Enable a network
*
* @param netId network id of the network
* @param disableOthers true, if all other networks have to be disabled
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
return sendSyncMessage(CMD_ENABLE_NETWORK,
new EnableNetParams(netId, disableOthers)).boolValue;
}
/**
* Disable a network
*
* @param netId network id of the network
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean disableNetwork(int netId) {
return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
}
/**
* Blacklist a BSSID. This will avoid the AP if there are
* alternate APs to connect
*
* @param bssid BSSID of the network
*/
public void addToBlacklist(String bssid) {
sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
}
/**
* Clear the blacklist list
*
*/
public void clearBlacklist() {
sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
}
/**
* Get detailed status of the connection
*
* @return Example status result
* bssid=aa:bb:cc:dd:ee:ff
* ssid=TestNet
* id=3
* pairwise_cipher=NONE
* group_cipher=NONE
* key_mgmt=NONE
* wpa_state=COMPLETED
* ip_address=X.X.X.X
*/
public String status() {
return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
}
public void enableRssiPolling(boolean enabled) {
sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
}
/**
* Get RSSI to currently connected network
*
* @return RSSI value, -1 on failure
*/
public int getRssi() {
return sendSyncMessage(CMD_GET_RSSI).intValue;
}
/**
* Get approx RSSI to currently connected network
*
* @return RSSI value, -1 on failure
*/
public int getRssiApprox() {
return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
}
/**
* Get link speed to currently connected network
*
* @return link speed, -1 on failure
*/
public int getLinkSpeed() {
return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
}
/**
* Get MAC address of radio
*
* @return MAC address, null on failure
*/
public String getMacAddress() {
return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
}
/**
* Start packet filtering
*/
public void startPacketFiltering() {
sendMessage(CMD_START_PACKET_FILTERING);
}
/**
* Stop packet filtering
*/
public void stopPacketFiltering() {
sendMessage(CMD_STOP_PACKET_FILTERING);
}
/**
* Set power mode
* @param mode
* DRIVER_POWER_MODE_AUTO
* DRIVER_POWER_MODE_ACTIVE
*/
public void setPowerMode(int mode) {
sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
}
/**
* Set the number of allowed radio frequency channels from the system
* setting value, if any.
*/
public void setNumAllowedChannels() {
try {
setNumAllowedChannels(
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
} catch (Settings.SettingNotFoundException e) {
if (mNumAllowedChannels != 0) {
setNumAllowedChannels(mNumAllowedChannels);
}
// otherwise, use the driver default
}
}
/**
* Set the number of radio frequency channels that are allowed to be used
* in the current regulatory domain.
* @param numChannels the number of allowed channels. Must be greater than 0
* and less than or equal to 16.
*/
public void setNumAllowedChannels(int numChannels) {
sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
}
/**
* Get number of allowed channels
*
* @return channel count, -1 on failure
*
* TODO: this is not a public API and needs to be removed in favor
* of asynchronous reporting. unused for now.
*/
public int getNumAllowedChannels() {
return -1;
}
/**
* Set bluetooth coex mode:
*
* @param mode
* BLUETOOTH_COEXISTENCE_MODE_ENABLED
* BLUETOOTH_COEXISTENCE_MODE_DISABLED
* BLUETOOTH_COEXISTENCE_MODE_SENSE
*/
public void setBluetoothCoexistenceMode(int mode) {
sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
}
/**
* Enable or disable Bluetooth coexistence scan mode. When this mode is on,
* some of the low-level scan parameters used by the driver are changed to
* reduce interference with A2DP streaming.
*
* @param isBluetoothPlaying whether to enable or disable this mode
*/
public void setBluetoothScanMode(boolean isBluetoothPlaying) {
sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
}
/**
* Save configuration on supplicant
*
* @return {@code true} if the operation succeeds, {@code false} otherwise
*
* TODO: deprecate this
*/
public boolean saveConfig() {
return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
}
/**
* TODO: doc
*/
public void requestCmWakeLock() {
sendMessage(CMD_REQUEST_CM_WAKELOCK);
}
/*********************************************************
* Internal private functions
********************************************************/
class SyncReturn {
boolean boolValue;
int intValue;
String stringValue;
Object objValue;
List<WifiConfiguration> configList;
}
class SyncParams {
Object mParameter;
SyncReturn mSyncReturn;
SyncParams() {
mSyncReturn = new SyncReturn();
}
SyncParams(Object p) {
mParameter = p;
mSyncReturn = new SyncReturn();
}
}
/**
* message.arg2 is reserved to indicate synchronized
* message.obj is used to store SyncParams
*/
private SyncReturn syncedSend(Message msg) {
SyncParams syncParams = (SyncParams) msg.obj;
msg.arg2 = SYNCHRONOUS_CALL;
synchronized(syncParams) {
if (DBG) Log.d(TAG, "syncedSend " + msg);
sendMessage(msg);
try {
syncParams.wait();
} catch (InterruptedException e) {
Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
return null;
}
}
return syncParams.mSyncReturn;
}
private SyncReturn sendSyncMessage(Message msg) {
SyncParams syncParams = new SyncParams();
msg.obj = syncParams;
return syncedSend(msg);
}
private SyncReturn sendSyncMessage(int what, Object param) {
SyncParams syncParams = new SyncParams(param);
Message msg = obtainMessage(what, syncParams);
return syncedSend(msg);
}
private SyncReturn sendSyncMessage(int what) {
return sendSyncMessage(obtainMessage(what));
}
private void notifyOnMsgObject(Message msg) {
SyncParams syncParams = (SyncParams) msg.obj;
if (syncParams != null) {
synchronized(syncParams) {
if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
syncParams.notify();
}
}
else {
Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
}
}
private void setWifiState(int wifiState) {
final int previousWifiState = mWifiState.get();
try {
if (wifiState == WIFI_STATE_ENABLED) {
mBatteryStats.noteWifiOn(mLastEnableUid.get());
} else if (wifiState == WIFI_STATE_DISABLED) {
mBatteryStats.noteWifiOff(mLastEnableUid.get());
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to note battery stats in wifi");
}
mWifiState.set(wifiState);
if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
mContext.sendStickyBroadcast(intent);
}
private void setWifiApState(int wifiApState) {
final int previousWifiApState = mWifiApState.get();
try {
if (wifiApState == WIFI_AP_STATE_ENABLED) {
mBatteryStats.noteWifiOn(mLastApEnableUid.get());
} else if (wifiApState == WIFI_AP_STATE_DISABLED) {
mBatteryStats.noteWifiOff(mLastApEnableUid.get());
}
} catch (RemoteException e) {
Log.d(TAG, "Failed to note battery stats in wifi");
}
// Update state
mWifiApState.set(wifiApState);
if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
// Broadcast
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
mContext.sendStickyBroadcast(intent);
}
/**
* Parse the scan result line passed to us by wpa_supplicant (helper).
* @param line the line to parse
* @return the {@link ScanResult} object
*/
private ScanResult parseScanResult(String line) {
ScanResult scanResult = null;
if (line != null) {
/*
* Cache implementation (LinkedHashMap) is not synchronized, thus,
* must synchronized here!
*/
synchronized (mScanResultCache) {
String[] result = scanResultPattern.split(line);
if (3 <= result.length && result.length <= 5) {
String bssid = result[0];
// bssid | frequency | level | flags | ssid
int frequency;
int level;
try {
frequency = Integer.parseInt(result[1]);
level = Integer.parseInt(result[2]);
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
if (level > 0) level -= 256;
} catch (NumberFormatException e) {
frequency = 0;
level = 0;
}
/*
* The formatting of the results returned by
* wpa_supplicant is intended to make the fields
* line up nicely when printed,
* not to make them easy to parse. So we have to
* apply some heuristics to figure out which field
* is the SSID and which field is the flags.
*/
String ssid;
String flags;
if (result.length == 4) {
if (result[3].charAt(0) == '[') {
flags = result[3];
ssid = "";
} else {
flags = "";
ssid = result[3];
}
} else if (result.length == 5) {
flags = result[3];
ssid = result[4];
} else {
// Here, we must have 3 fields: no flags and ssid
// set
flags = "";
ssid = "";
}
// bssid + ssid is the hash key
String key = bssid + ssid;
scanResult = mScanResultCache.get(key);
if (scanResult != null) {
scanResult.level = level;
scanResult.SSID = ssid;
scanResult.capabilities = flags;
scanResult.frequency = frequency;
} else {
// Do not add scan results that have no SSID set
if (0 < ssid.trim().length()) {
scanResult =
new ScanResult(
ssid, bssid, flags, level, frequency);
mScanResultCache.put(key, scanResult);
}
}
} else {
Log.w(TAG, "Misformatted scan result text with " +
result.length + " fields: " + line);
}
}
}
return scanResult;
}
/**
* scanResults input format
* 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
* 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
*/
private void setScanResults(String scanResults) {
if (scanResults == null) {
return;
}
List<ScanResult> scanList = new ArrayList<ScanResult>();
int lineCount = 0;
int scanResultsLen = scanResults.length();
// Parse the result string, keeping in mind that the last line does
// not end with a newline.
for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
++lineCount;
if (lineCount == 1) {
lineBeg = lineEnd + 1;
continue;
}
if (lineEnd > lineBeg) {
String line = scanResults.substring(lineBeg, lineEnd);
ScanResult scanResult = parseScanResult(line);
if (scanResult != null) {
scanList.add(scanResult);
} else {
Log.w(TAG, "misformatted scan result for: " + line);
}
}
lineBeg = lineEnd + 1;
}
}
mScanResults = scanList;
}
private void checkAndSetNotification() {
// If we shouldn't place a notification on available networks, then
// don't bother doing any of the following
if (!mNotificationEnabled) return;
State state = mNetworkInfo.getState();
if ((state == NetworkInfo.State.DISCONNECTED)
|| (state == NetworkInfo.State.UNKNOWN)) {
// Look for an open network
List<ScanResult> scanResults = mScanResults;
if (scanResults != null) {
int numOpenNetworks = 0;
for (int i = scanResults.size() - 1; i >= 0; i--) {
ScanResult scanResult = scanResults.get(i);
if (TextUtils.isEmpty(scanResult.capabilities)) {
numOpenNetworks++;
}
}
if (numOpenNetworks > 0) {
if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
/*
* We've scanned continuously at least
* NUM_SCANS_BEFORE_NOTIFICATION times. The user
* probably does not have a remembered network in range,
* since otherwise supplicant would have tried to
* associate and thus resetting this counter.
*/
setNotificationVisible(true, numOpenNetworks, false, 0);
}
return;
}
}
}
// No open networks in range, remove the notification
setNotificationVisible(false, 0, false, 0);
}
/**
* Display or don't display a notification that there are open Wi-Fi networks.
* @param visible {@code true} if notification should be visible, {@code false} otherwise
* @param numNetworks the number networks seen
* @param force {@code true} to force notification to be shown/not-shown,
* even if it is already shown/not-shown.
* @param delay time in milliseconds after which the notification should be made
* visible or invisible.
*/
private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
int delay) {
// Since we use auto cancel on the notification, when the
// mNetworksAvailableNotificationShown is true, the notification may
// have actually been canceled. However, when it is false we know
// for sure that it is not being shown (it will not be shown any other
// place than here)
// If it should be hidden and it is already hidden, then noop
if (!visible && !mNotificationShown && !force) {
return;
}
Message message;
if (visible) {
// Not enough time has passed to show the notification again
if (System.currentTimeMillis() < mNotificationRepeatTime) {
return;
}
if (mNotification == null) {
// Cache the Notification mainly so we can remove the
// EVENT_NOTIFICATION_CHANGED message with this Notification from
// the queue later
mNotification = new Notification();
mNotification.when = 0;
mNotification.icon = ICON_NETWORKS_AVAILABLE;
mNotification.flags = Notification.FLAG_AUTO_CANCEL;
mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
}
CharSequence title = mContext.getResources().getQuantityText(
com.android.internal.R.plurals.wifi_available, numNetworks);
CharSequence details = mContext.getResources().getQuantityText(
com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
mNotification.tickerText = title;
mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
ICON_NETWORKS_AVAILABLE, mNotification);
} else {
// Remove any pending messages to show the notification
mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
ICON_NETWORKS_AVAILABLE);
}
mCsHandler.sendMessageDelayed(message, delay);
mNotificationShown = visible;
}
private void configureNetworkProperties() {
try {
mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
} catch (SocketException e) {
Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
". e=" + e);
return;
} catch (NullPointerException e) {
Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
return;
}
// TODO - fix this for v6
try {
mNetworkProperties.addAddress(InetAddress.getByAddress(
NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
} catch (UnknownHostException e) {
Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
}
try {
mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
mDhcpInfo.gateway)));
} catch (UnknownHostException e) {
Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
}
try {
mNetworkProperties.addDns(InetAddress.getByAddress(
NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
} catch (UnknownHostException e) {
Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
}
try {
mNetworkProperties.addDns(InetAddress.getByAddress(
NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
} catch (UnknownHostException e) {
Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
}
// TODO - add proxy info
}
private void checkUseStaticIp() {
mUseStaticIp = false;
final ContentResolver cr = mContext.getContentResolver();
try {
if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
return;
}
} catch (Settings.SettingNotFoundException e) {
return;
}
try {
String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
if (addr != null) {
mDhcpInfo.ipAddress = stringToIpAddr(addr);
} else {
return;
}
addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
if (addr != null) {
mDhcpInfo.gateway = stringToIpAddr(addr);
} else {
return;
}
addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
if (addr != null) {
mDhcpInfo.netmask = stringToIpAddr(addr);
} else {
return;
}
addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
if (addr != null) {
mDhcpInfo.dns1 = stringToIpAddr(addr);
} else {
return;
}
addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
if (addr != null) {
mDhcpInfo.dns2 = stringToIpAddr(addr);
} else {
mDhcpInfo.dns2 = 0;
}
} catch (UnknownHostException e) {
return;
}
mUseStaticIp = true;
}
private static int stringToIpAddr(String addrString) throws UnknownHostException {
try {
String[] parts = addrString.split("\\.");
if (parts.length != 4) {
throw new UnknownHostException(addrString);
}
int a = Integer.parseInt(parts[0]) ;
int b = Integer.parseInt(parts[1]) << 8;
int c = Integer.parseInt(parts[2]) << 16;
int d = Integer.parseInt(parts[3]) << 24;
return a | b | c | d;
} catch (NumberFormatException ex) {
throw new UnknownHostException(addrString);
}
}
private int getMaxDhcpRetries() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
DEFAULT_MAX_DHCP_RETRIES);
}
private class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_USE_STATIC_IP), false, this);
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_STATIC_IP), false, this);
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_STATIC_GATEWAY), false, this);
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_STATIC_NETMASK), false, this);
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_STATIC_DNS1), false, this);
cr.registerContentObserver(Settings.System.getUriFor(
Settings.System.WIFI_STATIC_DNS2), false, this);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
boolean wasStaticIp = mUseStaticIp;
int oIp, oGw, oMsk, oDns1, oDns2;
oIp = oGw = oMsk = oDns1 = oDns2 = 0;
if (wasStaticIp) {
oIp = mDhcpInfo.ipAddress;
oGw = mDhcpInfo.gateway;
oMsk = mDhcpInfo.netmask;
oDns1 = mDhcpInfo.dns1;
oDns2 = mDhcpInfo.dns2;
}
checkUseStaticIp();
if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
return;
}
boolean changed =
(wasStaticIp != mUseStaticIp) ||
(wasStaticIp && (
oIp != mDhcpInfo.ipAddress ||
oGw != mDhcpInfo.gateway ||
oMsk != mDhcpInfo.netmask ||
oDns1 != mDhcpInfo.dns1 ||
oDns2 != mDhcpInfo.dns2));
if (changed) {
sendMessage(CMD_RECONFIGURE_IP);
if (mUseStaticIp) {
mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
}
}
}
}
/**
* Clears variables related to tracking whether a notification has been
* shown recently.
* <p>
* After calling this method, the timer that prevents notifications from
* being shown too often will be cleared.
*/
private void resetNotificationTimer() {
mNotificationRepeatTime = 0;
mNumScansSinceNetworkStateChange = 0;
}
/**
* Whether to disable coexistence mode while obtaining IP address. This
* logic will return true only if the current bluetooth
* headset/handsfree state is disconnected. This means if it is in an
* error state, we will NOT disable coexistence mode to err on the side
* of safety.
*
* @return Whether to disable coexistence mode.
*/
private boolean shouldDisableCoexistenceMode() {
int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
return state == BluetoothHeadset.STATE_DISCONNECTED;
}
private void checkIsBluetoothPlaying() {
boolean isBluetoothPlaying = false;
Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
for (BluetoothDevice device : connected) {
if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
isBluetoothPlaying = true;
break;
}
}
setBluetoothScanMode(isBluetoothPlaying);
}
private void sendScanResultsAvailableBroadcast() {
if (!ActivityManagerNative.isSystemReady()) return;
mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
private void sendRssiChangeBroadcast(final int newRssi) {
if (!ActivityManagerNative.isSystemReady()) return;
Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
mContext.sendBroadcast(intent);
}
private void sendNetworkStateChangeBroadcast(String bssid) {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
if (bssid != null)
intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
mContext.sendStickyBroadcast(intent);
}
private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
if (failedAuth) {
intent.putExtra(
WifiManager.EXTRA_SUPPLICANT_ERROR,
WifiManager.ERROR_AUTHENTICATING);
}
mContext.sendStickyBroadcast(intent);
}
private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
if (!ActivityManagerNative.isSystemReady()) return;
Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
mContext.sendBroadcast(intent);
}
/**
* Record the detailed state of a network, and if it is a
* change from the previous state, send a notification to
* any listeners.
* @param state the new @{code DetailedState}
*/
private void setDetailedState(NetworkInfo.DetailedState state) {
Log.d(TAG, "setDetailed state, old ="
+ mNetworkInfo.getDetailedState() + " and new state=" + state);
if (state != mNetworkInfo.getDetailedState()) {
mNetworkInfo.setDetailedState(state, null, null);
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
msg.sendToTarget();
}
}
private static String removeDoubleQuotes(String string) {
if (string.length() <= 2) return "";
return string.substring(1, string.length() - 1);
}
private static String convertToQuotedString(String string) {
return "\"" + string + "\"";
}
private static String makeString(BitSet set, String[] strings) {
StringBuffer buf = new StringBuffer();
int nextSetBit = -1;
/* Make sure all set bits are in [0, strings.length) to avoid
* going out of bounds on strings. (Shouldn't happen, but...) */
set = set.get(0, strings.length);
while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
}
// remove trailing space
if (set.cardinality() > 0) {
buf.setLength(buf.length() - 1);
}
return buf.toString();
}
private static int lookupString(String string, String[] strings) {
int size = strings.length;
string = string.replace('-', '_');
for (int i = 0; i < size; i++)
if (string.equals(strings[i]))
return i;
// if we ever get here, we should probably add the
// value to WifiConfiguration to reflect that it's
// supported by the WPA supplicant
Log.w(TAG, "Failed to look-up a string: " + string);
return -1;
}
private int addOrUpdateNetworkNative(WifiConfiguration config) {
/*
* If the supplied networkId is -1, we create a new empty
* network configuration. Otherwise, the networkId should
* refer to an existing configuration.
*/
int netId = config.networkId;
boolean newNetwork = netId == -1;
// networkId of -1 means we want to create a new network
if (newNetwork) {
netId = WifiNative.addNetworkCommand();
if (netId < 0) {
Log.e(TAG, "Failed to add a network!");
return -1;
}
}
setVariables: {
if (config.SSID != null &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.ssidVarName,
config.SSID)) {
Log.d(TAG, "failed to set SSID: "+config.SSID);
break setVariables;
}
if (config.BSSID != null &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.bssidVarName,
config.BSSID)) {
Log.d(TAG, "failed to set BSSID: "+config.BSSID);
break setVariables;
}
String allowedKeyManagementString =
makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
if (config.allowedKeyManagement.cardinality() != 0 &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.KeyMgmt.varName,
allowedKeyManagementString)) {
Log.d(TAG, "failed to set key_mgmt: "+
allowedKeyManagementString);
break setVariables;
}
String allowedProtocolsString =
makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
if (config.allowedProtocols.cardinality() != 0 &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.Protocol.varName,
allowedProtocolsString)) {
Log.d(TAG, "failed to set proto: "+
allowedProtocolsString);
break setVariables;
}
String allowedAuthAlgorithmsString =
makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
if (config.allowedAuthAlgorithms.cardinality() != 0 &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.AuthAlgorithm.varName,
allowedAuthAlgorithmsString)) {
Log.d(TAG, "failed to set auth_alg: "+
allowedAuthAlgorithmsString);
break setVariables;
}
String allowedPairwiseCiphersString =
makeString(config.allowedPairwiseCiphers,
WifiConfiguration.PairwiseCipher.strings);
if (config.allowedPairwiseCiphers.cardinality() != 0 &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.PairwiseCipher.varName,
allowedPairwiseCiphersString)) {
Log.d(TAG, "failed to set pairwise: "+
allowedPairwiseCiphersString);
break setVariables;
}
String allowedGroupCiphersString =
makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
if (config.allowedGroupCiphers.cardinality() != 0 &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.GroupCipher.varName,
allowedGroupCiphersString)) {
Log.d(TAG, "failed to set group: "+
allowedGroupCiphersString);
break setVariables;
}
// Prevent client screw-up by passing in a WifiConfiguration we gave it
// by preventing "*" as a key.
if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.pskVarName,
config.preSharedKey)) {
Log.d(TAG, "failed to set psk: "+config.preSharedKey);
break setVariables;
}
boolean hasSetKey = false;
if (config.wepKeys != null) {
for (int i = 0; i < config.wepKeys.length; i++) {
// Prevent client screw-up by passing in a WifiConfiguration we gave it
// by preventing "*" as a key.
if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
if (!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.wepKeyVarNames[i],
config.wepKeys[i])) {
Log.d(TAG,
"failed to set wep_key"+i+": " +
config.wepKeys[i]);
break setVariables;
}
hasSetKey = true;
}
}
}
if (hasSetKey) {
if (!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.wepTxKeyIdxVarName,
Integer.toString(config.wepTxKeyIndex))) {
Log.d(TAG,
"failed to set wep_tx_keyidx: "+
config.wepTxKeyIndex);
break setVariables;
}
}
if (!WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.priorityVarName,
Integer.toString(config.priority))) {
Log.d(TAG, config.SSID + ": failed to set priority: "
+config.priority);
break setVariables;
}
if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
netId,
WifiConfiguration.hiddenSSIDVarName,
Integer.toString(config.hiddenSSID ? 1 : 0))) {
Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
config.hiddenSSID);
break setVariables;
}
for (WifiConfiguration.EnterpriseField field
: config.enterpriseFields) {
String varName = field.varName();
String value = field.value();
if (value != null) {
if (field != config.eap) {
value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
}
if (!WifiNative.setNetworkVariableCommand(
netId,
varName,
value)) {
Log.d(TAG, config.SSID + ": failed to set " + varName +
": " + value);
break setVariables;
}
}
}
return netId;
}
if (newNetwork) {
WifiNative.removeNetworkCommand(netId);
Log.d(TAG,
"Failed to set a network variable, removed network: "
+ netId);
}
return -1;
}
private List<WifiConfiguration> getConfiguredNetworksNative() {
String listStr = WifiNative.listNetworksCommand();
List<WifiConfiguration> networks =
new ArrayList<WifiConfiguration>();
if (listStr == null)
return networks;
String[] lines = listStr.split("\n");
// Skip the first line, which is a header
for (int i = 1; i < lines.length; i++) {
String[] result = lines[i].split("\t");
// network-id | ssid | bssid | flags
WifiConfiguration config = new WifiConfiguration();
try {
config.networkId = Integer.parseInt(result[0]);
} catch(NumberFormatException e) {
continue;
}
if (result.length > 3) {
if (result[3].indexOf("[CURRENT]") != -1)
config.status = WifiConfiguration.Status.CURRENT;
else if (result[3].indexOf("[DISABLED]") != -1)
config.status = WifiConfiguration.Status.DISABLED;
else
config.status = WifiConfiguration.Status.ENABLED;
} else {
config.status = WifiConfiguration.Status.ENABLED;
}
readNetworkVariables(config);
networks.add(config);
}
return networks;
}
/**
* Read the variables from the supplicant daemon that are needed to
* fill in the WifiConfiguration object.
*
* @param config the {@link WifiConfiguration} object to be filled in.
*/
private void readNetworkVariables(WifiConfiguration config) {
int netId = config.networkId;
if (netId < 0)
return;
/*
* TODO: maybe should have a native method that takes an array of
* variable names and returns an array of values. But we'd still
* be doing a round trip to the supplicant daemon for each variable.
*/
String value;
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
config.SSID = removeDoubleQuotes(value);
} else {
config.SSID = null;
}
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
if (!TextUtils.isEmpty(value)) {
config.BSSID = value;
} else {
config.BSSID = null;
}
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
config.priority = -1;
if (!TextUtils.isEmpty(value)) {
try {
config.priority = Integer.parseInt(value);
} catch (NumberFormatException ignore) {
}
}
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
config.hiddenSSID = false;
if (!TextUtils.isEmpty(value)) {
try {
config.hiddenSSID = Integer.parseInt(value) != 0;
} catch (NumberFormatException ignore) {
}
}
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
config.wepTxKeyIndex = -1;
if (!TextUtils.isEmpty(value)) {
try {
config.wepTxKeyIndex = Integer.parseInt(value);
} catch (NumberFormatException ignore) {
}
}
for (int i = 0; i < 4; i++) {
value = WifiNative.getNetworkVariableCommand(netId,
WifiConfiguration.wepKeyVarNames[i]);
if (!TextUtils.isEmpty(value)) {
config.wepKeys[i] = value;
} else {
config.wepKeys[i] = null;
}
}
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
if (!TextUtils.isEmpty(value)) {
config.preSharedKey = value;
} else {
config.preSharedKey = null;
}
value = WifiNative.getNetworkVariableCommand(config.networkId,
WifiConfiguration.Protocol.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
for (String val : vals) {
int index =
lookupString(val, WifiConfiguration.Protocol.strings);
if (0 <= index) {
config.allowedProtocols.set(index);
}
}
}
value = WifiNative.getNetworkVariableCommand(config.networkId,
WifiConfiguration.KeyMgmt.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
for (String val : vals) {
int index =
lookupString(val, WifiConfiguration.KeyMgmt.strings);
if (0 <= index) {
config.allowedKeyManagement.set(index);
}
}
}
value = WifiNative.getNetworkVariableCommand(config.networkId,
WifiConfiguration.AuthAlgorithm.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
for (String val : vals) {
int index =
lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
if (0 <= index) {
config.allowedAuthAlgorithms.set(index);
}
}
}
value = WifiNative.getNetworkVariableCommand(config.networkId,
WifiConfiguration.PairwiseCipher.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
for (String val : vals) {
int index =
lookupString(val, WifiConfiguration.PairwiseCipher.strings);
if (0 <= index) {
config.allowedPairwiseCiphers.set(index);
}
}
}
value = WifiNative.getNetworkVariableCommand(config.networkId,
WifiConfiguration.GroupCipher.varName);
if (!TextUtils.isEmpty(value)) {
String vals[] = value.split(" ");
for (String val : vals) {
int index =
lookupString(val, WifiConfiguration.GroupCipher.strings);
if (0 <= index) {
config.allowedGroupCiphers.set(index);
}
}
}
for (WifiConfiguration.EnterpriseField field :
config.enterpriseFields) {
value = WifiNative.getNetworkVariableCommand(netId,
field.varName());
if (!TextUtils.isEmpty(value)) {
if (field != config.eap) value = removeDoubleQuotes(value);
field.setValue(value);
}
}
}
/**
* Poll for info not reported via events
* RSSI & Linkspeed
*/
private void requestPolledInfo() {
int newRssi = WifiNative.getRssiCommand();
if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
if (newRssi > 0) newRssi -= 256;
mWifiInfo.setRssi(newRssi);
/*
* Rather then sending the raw RSSI out every time it
* changes, we precalculate the signal level that would
* be displayed in the status bar, and only send the
* broadcast if that much more coarse-grained number
* changes. This cuts down greatly on the number of
* broadcasts, at the cost of not mWifiInforming others
* interested in RSSI of all the changes in signal
* level.
*/
// TODO: The second arg to the call below needs to be a symbol somewhere, but
// it's actually the size of an array of icons that's private
// to StatusBar Policy.
int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
if (newSignalLevel != mLastSignalLevel) {
sendRssiChangeBroadcast(newRssi);
}
mLastSignalLevel = newSignalLevel;
} else {
mWifiInfo.setRssi(-200);
}
int newLinkSpeed = WifiNative.getLinkSpeedCommand();
if (newLinkSpeed != -1) {
mWifiInfo.setLinkSpeed(newLinkSpeed);
}
}
/**
* Resets the Wi-Fi Connections by clearing any state, resetting any sockets
* using the interface, stopping DHCP & disabling interface
*/
private void handleNetworkDisconnect() {
Log.d(TAG, "Reset connections and stopping DHCP");
/*
* Reset connections & stop DHCP
*/
NetworkUtils.resetConnections(mInterfaceName);
if (!NetworkUtils.stopDhcp(mInterfaceName)) {
Log.e(TAG, "Could not stop DHCP");
}
/* Disable interface */
NetworkUtils.disableInterface(mInterfaceName);
/* send event to CM & network change broadcast */
setDetailedState(DetailedState.DISCONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
/* Reset data structures */
mWifiInfo.setIpAddress(0);
mWifiInfo.setBSSID(null);
mWifiInfo.setSSID(null);
mWifiInfo.setNetworkId(-1);
/* Clear network properties */
mNetworkProperties.clear();
mLastBssid= null;
mLastNetworkId = -1;
}
/*********************************************************
* Notifications from WifiMonitor
********************************************************/
/**
* A structure for supplying information about a supplicant state
* change in the STATE_CHANGE event message that comes from the
* WifiMonitor
* thread.
*/
private static class StateChangeResult {
StateChangeResult(int networkId, String BSSID, Object state) {
this.state = state;
this.BSSID = BSSID;
this.networkId = networkId;
}
int networkId;
String BSSID;
Object state;
}
/**
* Send the tracker a notification that a user-entered password key
* may be incorrect (i.e., caused authentication to fail).
*/
void notifyPasswordKeyMayBeIncorrect() {
sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
}
/**
* Send the tracker a notification that a connection to the supplicant
* daemon has been established.
*/
void notifySupplicantConnection() {
sendMessage(SUP_CONNECTION_EVENT);
}
/**
* Send the tracker a notification that a connection to the supplicant
* daemon has been established.
*/
void notifySupplicantLost() {
sendMessage(SUP_DISCONNECTION_EVENT);
}
/**
* Send the tracker a notification that the state of Wifi connectivity
* has changed.
* @param networkId the configured network on which the state change occurred
* @param newState the new network state
* @param BSSID when the new state is {@link DetailedState#CONNECTED
* NetworkInfo.DetailedState.CONNECTED},
* this is the MAC address of the access point. Otherwise, it
* is {@code null}.
*/
void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
if (newState == NetworkInfo.DetailedState.CONNECTED) {
sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
new StateChangeResult(networkId, BSSID, newState)));
} else {
sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
new StateChangeResult(networkId, BSSID, newState)));
}
}
/**
* Send the tracker a notification that the state of the supplicant
* has changed.
* @param networkId the configured network on which the state change occurred
* @param newState the new {@code SupplicantState}
*/
void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
new StateChangeResult(networkId, BSSID, newState)));
}
/**
* Send the tracker a notification that a scan has completed, and results
* are available.
*/
void notifyScanResultsAvailable() {
/**
* Switch scan mode over to passive.
* Turning off scan-only mode happens only in "Connect" mode
*/
setScanType(false);
sendMessage(SCAN_RESULTS_EVENT);
}
void notifyDriverStarted() {
sendMessage(DRIVER_START_EVENT);
}
void notifyDriverStopped() {
sendMessage(DRIVER_STOP_EVENT);
}
void notifyDriverHung() {
setWifiEnabled(false);
setWifiEnabled(true);
}
/********************************************************
* HSM states
*******************************************************/
class DefaultState extends HierarchicalState {
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
SyncParams syncParams;
switch (message.what) {
/* Synchronous call returns */
case CMD_PING_SUPPLICANT:
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
case CMD_REMOVE_NETWORK:
case CMD_ENABLE_NETWORK:
case CMD_DISABLE_NETWORK:
case CMD_ADD_OR_UPDATE_NETWORK:
case CMD_GET_RSSI:
case CMD_GET_RSSI_APPROX:
case CMD_GET_LINK_SPEED:
case CMD_GET_MAC_ADDR:
case CMD_SAVE_CONFIG:
case CMD_CONNECTION_STATUS:
case CMD_GET_NETWORK_CONFIG:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = false;
syncParams.mSyncReturn.intValue = -1;
syncParams.mSyncReturn.stringValue = null;
syncParams.mSyncReturn.configList = null;
notifyOnMsgObject(message);
}
break;
case CM_CMD_TEARDOWN:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
mTeardownRequested.set(true);
sendMessage(CMD_DISCONNECT);
sendMessage(CMD_STOP_DRIVER);
/* Mark wifi available when CM tears down */
mNetworkInfo.setIsAvailable(true);
break;
case CM_CMD_RECONNECT:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
mTeardownRequested.set(false);
sendMessage(CMD_START_DRIVER);
sendMessage(CMD_RECONNECT);
break;
case CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
break;
default:
if (DBG) Log.w(TAG, "Unhandled " + message);
break;
}
return HANDLED;
}
}
class InitialState extends HierarchicalState {
@Override
//TODO: could move logging into a common class
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
// [31-8] Reserved for future use
// [7 - 0] HSM state change
// 50021 wifi_state_changed (custom|1|5)
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
if (WifiNative.isDriverLoaded()) {
transitionTo(mDriverLoadedState);
}
else {
transitionTo(mDriverUnloadedState);
}
}
}
class DriverLoadingState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
final Message message = new Message();
message.copyFrom(getCurrentMessage());
new Thread(new Runnable() {
public void run() {
sWakeLock.acquire();
//enabling state
switch(message.arg1) {
case WIFI_STATE_ENABLING:
setWifiState(WIFI_STATE_ENABLING);
break;
case WIFI_AP_STATE_ENABLING:
setWifiApState(WIFI_AP_STATE_ENABLING);
break;
}
if(WifiNative.loadDriver()) {
Log.d(TAG, "Driver load successful");
sendMessage(CMD_LOAD_DRIVER_SUCCESS);
} else {
Log.e(TAG, "Failed to load driver!");
switch(message.arg1) {
case WIFI_STATE_ENABLING:
setWifiState(WIFI_STATE_UNKNOWN);
break;
case WIFI_AP_STATE_ENABLING:
setWifiApState(WIFI_AP_STATE_FAILED);
break;
}
sendMessage(CMD_LOAD_DRIVER_FAILURE);
}
sWakeLock.release();
}
}).start();
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_LOAD_DRIVER_SUCCESS:
transitionTo(mDriverLoadedState);
break;
case CMD_LOAD_DRIVER_FAILURE:
transitionTo(mDriverFailedState);
break;
case CMD_LOAD_DRIVER:
case CMD_UNLOAD_DRIVER:
case CMD_START_SUPPLICANT:
case CMD_STOP_SUPPLICANT:
case CMD_START_AP:
case CMD_STOP_AP:
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
case CMD_SET_POWER_MODE:
case CMD_SET_BLUETOOTH_COEXISTENCE:
case CMD_SET_BLUETOOTH_SCAN_MODE:
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
deferMessage(message);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverLoadedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case CMD_UNLOAD_DRIVER:
transitionTo(mDriverUnloadingState);
break;
case CMD_START_SUPPLICANT:
if(WifiNative.startSupplicant()) {
Log.d(TAG, "Supplicant start successful");
mWifiMonitor.startMonitoring();
setWifiState(WIFI_STATE_ENABLED);
transitionTo(mWaitForSupState);
} else {
Log.e(TAG, "Failed to start supplicant!");
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
}
break;
case CMD_START_AP:
try {
nwService.startAccessPoint((WifiConfiguration) message.obj,
mInterfaceName,
SOFTAP_IFACE);
} catch(Exception e) {
Log.e(TAG, "Exception in startAccessPoint()");
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
break;
}
Log.d(TAG, "Soft AP start successful");
setWifiApState(WIFI_AP_STATE_ENABLED);
transitionTo(mSoftApStartedState);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverUnloadingState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
final Message message = new Message();
message.copyFrom(getCurrentMessage());
new Thread(new Runnable() {
public void run() {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
sWakeLock.acquire();
if(WifiNative.unloadDriver()) {
Log.d(TAG, "Driver unload successful");
sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
switch(message.arg1) {
case WIFI_STATE_DISABLED:
case WIFI_STATE_UNKNOWN:
setWifiState(message.arg1);
break;
case WIFI_AP_STATE_DISABLED:
case WIFI_AP_STATE_FAILED:
setWifiApState(message.arg1);
break;
}
} else {
Log.e(TAG, "Failed to unload driver!");
sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
switch(message.arg1) {
case WIFI_STATE_DISABLED:
case WIFI_STATE_UNKNOWN:
setWifiState(WIFI_STATE_UNKNOWN);
break;
case WIFI_AP_STATE_DISABLED:
case WIFI_AP_STATE_FAILED:
setWifiApState(WIFI_AP_STATE_FAILED);
break;
}
}
sWakeLock.release();
}
}).start();
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_UNLOAD_DRIVER_SUCCESS:
transitionTo(mDriverUnloadedState);
break;
case CMD_UNLOAD_DRIVER_FAILURE:
transitionTo(mDriverFailedState);
break;
case CMD_LOAD_DRIVER:
case CMD_UNLOAD_DRIVER:
case CMD_START_SUPPLICANT:
case CMD_STOP_SUPPLICANT:
case CMD_START_AP:
case CMD_STOP_AP:
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
case CMD_SET_POWER_MODE:
case CMD_SET_BLUETOOTH_COEXISTENCE:
case CMD_SET_BLUETOOTH_SCAN_MODE:
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
deferMessage(message);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverUnloadedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_LOAD_DRIVER:
transitionTo(mDriverLoadingState);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverFailedState extends HierarchicalState {
@Override
public void enter() {
Log.e(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
return NOT_HANDLED;
}
}
class WaitForSupState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case SUP_CONNECTION_EVENT:
Log.d(TAG, "Supplicant connection established");
mSupplicantStateTracker.resetSupplicantState();
/* Initialize data structures */
resetNotificationTimer();
setTeardownRequested(false);
mLastBssid = null;
mLastNetworkId = -1;
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
//TODO: initialize and fix multicast filtering
//mWM.initializeMulticastFiltering();
if (mBluetoothA2dp == null) {
mBluetoothA2dp = new BluetoothA2dp(mContext);
}
checkIsBluetoothPlaying();
checkUseStaticIp();
sendSupplicantConnectionChangedBroadcast(true);
transitionTo(mDriverSupReadyState);
break;
case CMD_STOP_SUPPLICANT:
Log.d(TAG, "Stop supplicant received");
WifiNative.stopSupplicant();
transitionTo(mDriverLoadedState);
break;
/* Fail soft ap when waiting for supplicant start */
case CMD_START_AP:
Log.d(TAG, "Failed to start soft AP with a running supplicant");
setWifiApState(WIFI_AP_STATE_FAILED);
break;
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_MODE:
case CMD_SET_SCAN_TYPE:
case CMD_SET_POWER_MODE:
case CMD_SET_BLUETOOTH_COEXISTENCE:
case CMD_SET_BLUETOOTH_SCAN_MODE:
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
deferMessage(message);
break;
case CMD_STOP_AP:
case CMD_START_SUPPLICANT:
case CMD_UNLOAD_DRIVER:
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverSupReadyState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
/* Initialize for connect mode operation at start */
mIsScanMode = false;
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
SyncParams syncParams;
switch(message.what) {
case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
Log.d(TAG, "Stop supplicant received");
WifiNative.stopSupplicant();
//$FALL-THROUGH$
case SUP_DISCONNECTION_EVENT: /* Supplicant died */
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
//Remove any notifications on disconnection
setNotificationVisible(false, 0, false, 0);
WifiNative.closeSupplicantConnection();
handleNetworkDisconnect();
sendSupplicantConnectionChangedBroadcast(false);
mSupplicantStateTracker.resetSupplicantState();
transitionTo(mDriverLoadedState);
/* When supplicant dies, unload driver and enter failed state */
//TODO: consider bringing up supplicant again
if (message.what == SUP_DISCONNECTION_EVENT) {
Log.d(TAG, "Supplicant died, unloading driver");
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
}
break;
case CMD_START_DRIVER:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
WifiNative.startDriverCommand();
transitionTo(mDriverStartingState);
break;
case SCAN_RESULTS_EVENT:
setScanResults(WifiNative.scanResultsCommand());
sendScanResultsAvailableBroadcast();
checkAndSetNotification();
break;
case CMD_PING_SUPPLICANT:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
notifyOnMsgObject(message);
break;
case CMD_ADD_OR_UPDATE_NETWORK:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
syncParams = (SyncParams) message.obj;
WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
notifyOnMsgObject(message);
break;
case CMD_REMOVE_NETWORK:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
message.arg1);
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.removeNetworkCommand(message.arg1);
}
break;
case CMD_ENABLE_NETWORK:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
enableNetParams.netId, enableNetParams.disableOthers);
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
}
break;
case CMD_DISABLE_NETWORK:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
message.arg1);
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.disableNetworkCommand(message.arg1);
}
break;
case CMD_BLACKLIST_NETWORK:
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
WifiNative.addToBlacklistCommand((String)message.obj);
break;
case CMD_CLEAR_BLACKLIST:
WifiNative.clearBlacklistCommand();
break;
case CMD_GET_NETWORK_CONFIG:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
notifyOnMsgObject(message);
break;
case CMD_SAVE_CONFIG:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.saveConfigCommand();
}
// Inform the backup manager about a data change
IBackupManager ibm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (ibm != null) {
try {
ibm.dataChanged("com.android.providers.settings");
} catch (Exception e) {
// Try again later
}
}
break;
case CMD_CONNECTION_STATUS:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
notifyOnMsgObject(message);
break;
case CMD_GET_MAC_ADDR:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
notifyOnMsgObject(message);
break;
/* Cannot start soft AP while in client mode */
case CMD_START_AP:
Log.d(TAG, "Failed to start soft AP with a running supplicant");
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
setWifiApState(WIFI_AP_STATE_FAILED);
break;
case CMD_SET_SCAN_MODE:
mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
class DriverStartingState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case DRIVER_START_EVENT:
transitionTo(mDriverStartedState);
break;
/* Queue driver commands & connection events */
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case SUPPLICANT_STATE_CHANGE_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case PASSWORD_MAY_BE_INCORRECT_EVENT:
case CMD_SET_SCAN_TYPE:
case CMD_SET_POWER_MODE:
case CMD_SET_BLUETOOTH_COEXISTENCE:
case CMD_SET_BLUETOOTH_SCAN_MODE:
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
deferMessage(message);
break;
/* Queue the asynchronous version of these commands */
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_REASSOCIATE:
case CMD_RECONNECT:
if (message.arg2 != SYNCHRONOUS_CALL) {
deferMessage(message);
}
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverStartedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
try {
mBatteryStats.noteWifiRunning();
} catch (RemoteException ignore) {}
/* Initialize channel count */
setNumAllowedChannels();
if (mIsScanMode) {
WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
WifiNative.disconnectCommand();
transitionTo(mScanModeState);
} else {
WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
WifiNative.reconnectCommand();
transitionTo(mConnectModeState);
}
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
SyncParams syncParams;
switch(message.what) {
case CMD_SET_SCAN_TYPE:
if (message.arg1 == SCAN_ACTIVE) {
WifiNative.setScanModeCommand(true);
} else {
WifiNative.setScanModeCommand(false);
}
break;
case CMD_SET_POWER_MODE:
WifiNative.setPowerModeCommand(message.arg1);
break;
case CMD_SET_BLUETOOTH_COEXISTENCE:
WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
break;
case CMD_SET_BLUETOOTH_SCAN_MODE:
WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
break;
case CMD_SET_NUM_ALLOWED_CHANNELS:
mNumAllowedChannels = message.arg1;
WifiNative.setNumAllowedChannelsCommand(message.arg1);
break;
case CMD_START_DRIVER:
/* Ignore another driver start */
break;
case CMD_STOP_DRIVER:
WifiNative.stopDriverCommand();
transitionTo(mDriverStoppingState);
break;
case CMD_REQUEST_CM_WAKELOCK:
if (mCm == null) {
mCm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
}
mCm.requestNetworkTransitionWakelock(TAG);
break;
case CMD_START_PACKET_FILTERING:
WifiNative.startPacketFiltering();
break;
case CMD_STOP_PACKET_FILTERING:
WifiNative.stopPacketFiltering();
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
@Override
public void exit() {
if (DBG) Log.d(TAG, getName() + "\n");
try {
mBatteryStats.noteWifiStopped();
} catch (RemoteException ignore) { }
}
}
class DriverStoppingState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case DRIVER_STOP_EVENT:
transitionTo(mDriverStoppedState);
break;
/* Queue driver commands */
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_SCAN_TYPE:
case CMD_SET_POWER_MODE:
case CMD_SET_BLUETOOTH_COEXISTENCE:
case CMD_SET_BLUETOOTH_SCAN_MODE:
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
deferMessage(message);
break;
/* Queue the asynchronous version of these commands */
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_REASSOCIATE:
case CMD_RECONNECT:
if (message.arg2 != SYNCHRONOUS_CALL) {
deferMessage(message);
}
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DriverStoppedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
// Take down any open network notifications on driver stop
setNotificationVisible(false, 0, false, 0);
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
return NOT_HANDLED;
}
}
class ScanModeState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
SyncParams syncParams;
switch(message.what) {
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
/* Ignore */
return HANDLED;
} else {
WifiNative.setScanResultHandlingCommand(message.arg1);
WifiNative.reconnectCommand();
mIsScanMode = false;
transitionTo(mDisconnectedState);
}
break;
case CMD_START_SCAN:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
message.arg1 == SCAN_ACTIVE);
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
}
break;
/* Ignore */
case CMD_DISCONNECT:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
case SUPPLICANT_STATE_CHANGE_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class ConnectModeState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
SyncParams syncParams;
StateChangeResult stateChangeResult;
switch(message.what) {
case PASSWORD_MAY_BE_INCORRECT_EVENT:
mPasswordKeyMayBeIncorrect = true;
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
stateChangeResult = (StateChangeResult) message.obj;
mSupplicantStateTracker.handleEvent(stateChangeResult);
break;
case CMD_START_SCAN:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = true;
notifyOnMsgObject(message);
}
/* We need to set scan type in completed state */
Message newMsg = obtainMessage();
newMsg.copyFrom(message);
mSupplicantStateTracker.sendMessage(newMsg);
break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.disconnectCommand();
}
break;
case CMD_RECONNECT:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.reconnectCommand();
}
break;
case CMD_REASSOCIATE:
if (message.arg2 == SYNCHRONOUS_CALL) {
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.reassociateCommand();
}
break;
case SCAN_RESULTS_EVENT:
/* Set the scan setting back to "connect" mode */
WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
/* Handle scan results */
return NOT_HANDLED;
case NETWORK_CONNECTION_EVENT:
Log.d(TAG,"Network connection established");
stateChangeResult = (StateChangeResult) message.obj;
/* Remove any notifications */
setNotificationVisible(false, 0, false, 0);
resetNotificationTimer();
mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
mWifiInfo.setNetworkId(stateChangeResult.networkId);
mLastNetworkId = stateChangeResult.networkId;
/* send event to CM & network change broadcast */
setDetailedState(DetailedState.OBTAINING_IPADDR);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectingState);
break;
case NETWORK_DISCONNECTION_EVENT:
Log.d(TAG,"Network connection lost");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
case CMD_GET_RSSI:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
notifyOnMsgObject(message);
break;
case CMD_GET_RSSI_APPROX:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
notifyOnMsgObject(message);
break;
case CMD_GET_LINK_SPEED:
syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
notifyOnMsgObject(message);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class ConnectingState extends HierarchicalState {
boolean modifiedBluetoothCoexistenceMode;
int powerMode;
Thread mDhcpThread;
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
if (!mUseStaticIp) {
mDhcpThread = null;
modifiedBluetoothCoexistenceMode = false;
powerMode = DRIVER_POWER_MODE_AUTO;
if (shouldDisableCoexistenceMode()) {
/*
* There are problems setting the Wi-Fi driver's power
* mode to active when bluetooth coexistence mode is
* enabled or sense.
* <p>
* We set Wi-Fi to active mode when
* obtaining an IP address because we've found
* compatibility issues with some routers with low power
* mode.
* <p>
* In order for this active power mode to properly be set,
* we disable coexistence mode until we're done with
* obtaining an IP address. One exception is if we
* are currently connected to a headset, since disabling
* coexistence would interrupt that connection.
*/
modifiedBluetoothCoexistenceMode = true;
// Disable the coexistence mode
WifiNative.setBluetoothCoexistenceModeCommand(
WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
powerMode = WifiNative.getPowerModeCommand();
if (powerMode < 0) {
// Handle the case where supplicant driver does not support
// getPowerModeCommand.
powerMode = DRIVER_POWER_MODE_AUTO;
}
if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
}
Log.d(TAG, "DHCP request started");
mDhcpThread = new Thread(new Runnable() {
public void run() {
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
Log.d(TAG, "DHCP request succeeded");
sendMessage(CMD_IP_CONFIG_SUCCESS);
} else {
Log.d(TAG, "DHCP request failed: " +
NetworkUtils.getDhcpError());
sendMessage(CMD_IP_CONFIG_FAILURE);
}
}
});
mDhcpThread.start();
} else {
if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
Log.v(TAG, "Static IP configuration succeeded");
sendMessage(CMD_IP_CONFIG_SUCCESS);
} else {
Log.v(TAG, "Static IP configuration failed");
sendMessage(CMD_IP_CONFIG_FAILURE);
}
}
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case CMD_IP_CONFIG_SUCCESS:
mReconnectCount = 0;
mLastSignalLevel = -1; // force update of signal strength
mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
Log.d(TAG, "IP configuration: " + mDhcpInfo);
configureNetworkProperties();
setDetailedState(DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectedState);
break;
case CMD_IP_CONFIG_FAILURE:
mWifiInfo.setIpAddress(0);
Log.e(TAG, "IP configuration failed");
/**
* If we've exceeded the maximum number of retries for DHCP
* to a given network, disable the network
*/
if (++mReconnectCount > getMaxDhcpRetries()) {
Log.e(TAG, "Failed " +
mReconnectCount + " times, Disabling " + mLastNetworkId);
WifiNative.disableNetworkCommand(mLastNetworkId);
}
/* DHCP times out after about 30 seconds, we do a
* disconnect and an immediate reconnect to try again
*/
WifiNative.disconnectCommand();
WifiNative.reconnectCommand();
transitionTo(mDisconnectingState);
break;
case CMD_DISCONNECT:
if (message.arg2 == SYNCHRONOUS_CALL) {
SyncParams syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.disconnectCommand();
}
transitionTo(mDisconnectingState);
break;
/* Ignore */
case NETWORK_CONNECTION_EVENT:
break;
case CMD_STOP_DRIVER:
sendMessage(CMD_DISCONNECT);
deferMessage(message);
break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
sendMessage(CMD_DISCONNECT);
deferMessage(message);
}
break;
case CMD_RECONFIGURE_IP:
deferMessage(message);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
@Override
public void exit() {
/* reset power state & bluetooth coexistence if on DHCP */
if (!mUseStaticIp) {
if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
WifiNative.setPowerModeCommand(powerMode);
}
if (modifiedBluetoothCoexistenceMode) {
// Set the coexistence mode back to its default value
WifiNative.setBluetoothCoexistenceModeCommand(
WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
}
}
}
}
class ConnectedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_DISCONNECT:
if (message.arg2 == SYNCHRONOUS_CALL) {
SyncParams syncParams = (SyncParams) message.obj;
syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
notifyOnMsgObject(message);
} else {
/* asynchronous handling */
WifiNative.disconnectCommand();
}
transitionTo(mDisconnectingState);
break;
case CMD_RECONFIGURE_IP:
Log.d(TAG,"Reconfiguring IP on connection");
NetworkUtils.resetConnections(mInterfaceName);
transitionTo(mConnectingState);
break;
case CMD_STOP_DRIVER:
sendMessage(CMD_DISCONNECT);
deferMessage(message);
break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
sendMessage(CMD_DISCONNECT);
deferMessage(message);
}
break;
/* Ignore */
case NETWORK_CONNECTION_EVENT:
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DisconnectingState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
deferMessage(message);
break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
deferMessage(message);
}
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class DisconnectedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
WifiNative.setScanResultHandlingCommand(message.arg1);
//Supplicant disconnect to prevent further connects
WifiNative.disconnectCommand();
mIsScanMode = true;
transitionTo(mScanModeState);
}
break;
/* Ignore network disconnect */
case NETWORK_DISCONNECTION_EVENT:
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class SoftApStartedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
case CMD_STOP_AP:
Log.d(TAG,"Stopping Soft AP");
setWifiApState(WIFI_AP_STATE_DISABLING);
try {
nwService.stopAccessPoint();
} catch(Exception e) {
Log.e(TAG, "Exception in stopAccessPoint()");
}
transitionTo(mDriverLoadedState);
break;
case CMD_START_AP:
Log.d(TAG,"SoftAP set on a running access point");
try {
nwService.setAccessPoint((WifiConfiguration) message.obj,
mInterfaceName,
SOFTAP_IFACE);
} catch(Exception e) {
Log.e(TAG, "Exception in nwService during soft AP set");
try {
nwService.stopAccessPoint();
} catch (Exception ee) {
Slog.e(TAG, "Could not stop AP, :" + ee);
}
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
}
break;
/* Fail client mode operation when soft AP is enabled */
case CMD_START_SUPPLICANT:
Log.e(TAG,"Cannot start supplicant with a running soft AP");
setWifiState(WIFI_STATE_UNKNOWN);
break;
default:
return NOT_HANDLED;
}
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
return HANDLED;
}
}
class SupplicantStateTracker extends HierarchicalStateMachine {
private int mRssiPollToken = 0;
/**
* The max number of the WPA supplicant loop iterations before we
* decide that the loop should be terminated:
*/
private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
private int mLoopDetectIndex = 0;
private int mLoopDetectCount = 0;
/**
* Supplicant state change commands follow
* the ordinal values defined in SupplicantState.java
*/
private static final int DISCONNECTED = 0;
private static final int INACTIVE = 1;
private static final int SCANNING = 2;
private static final int ASSOCIATING = 3;
private static final int ASSOCIATED = 4;
private static final int FOUR_WAY_HANDSHAKE = 5;
private static final int GROUP_HANDSHAKE = 6;
private static final int COMPLETED = 7;
private static final int DORMANT = 8;
private static final int UNINITIALIZED = 9;
private static final int INVALID = 10;
private HierarchicalState mUninitializedState;
private HierarchicalState mInitializedState;
private HierarchicalState mInactiveState;
private HierarchicalState mDisconnectState;
private HierarchicalState mScanState;
private HierarchicalState mConnectState;
private HierarchicalState mHandshakeState;
private HierarchicalState mCompletedState;
private HierarchicalState mDormantState;
public SupplicantStateTracker(Context context, Handler target) {
super(TAG, target.getLooper());
mUninitializedState = new UninitializedState();
mInitializedState = new InitializedState();
mInactiveState = new InactiveState();
mDisconnectState = new DisconnectedState();
mScanState = new ScanState();
mConnectState = new ConnectState();
mHandshakeState = new HandshakeState();
mCompletedState = new CompletedState();
mDormantState = new DormantState();
addState(mUninitializedState);
addState(mInitializedState);
addState(mInactiveState, mInitializedState);
addState(mDisconnectState, mInitializedState);
addState(mScanState, mInitializedState);
addState(mConnectState, mInitializedState);
addState(mHandshakeState, mConnectState);
addState(mCompletedState, mConnectState);
addState(mDormantState, mInitializedState);
setInitialState(mUninitializedState);
//start the state machine
start();
}
public void handleEvent(StateChangeResult stateChangeResult) {
SupplicantState newState = (SupplicantState) stateChangeResult.state;
// Supplicant state change
// [31-13] Reserved for future use
// [8 - 0] Supplicant state (as defined in SupplicantState.java)
// 50023 supplicant_state_changed (custom|1|5)
EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
}
public void resetSupplicantState() {
transitionTo(mUninitializedState);
}
private void resetLoopDetection() {
mLoopDetectCount = 0;
mLoopDetectIndex = 0;
}
private boolean handleTransition(Message msg) {
if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
switch (msg.what) {
case DISCONNECTED:
transitionTo(mDisconnectState);
break;
case SCANNING:
transitionTo(mScanState);
break;
case ASSOCIATING:
StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
/* BSSID is valid only in ASSOCIATING state */
mWifiInfo.setBSSID(stateChangeResult.BSSID);
//$FALL-THROUGH$
case ASSOCIATED:
case FOUR_WAY_HANDSHAKE:
case GROUP_HANDSHAKE:
transitionTo(mHandshakeState);
break;
case COMPLETED:
transitionTo(mCompletedState);
break;
case DORMANT:
transitionTo(mDormantState);
break;
case INACTIVE:
transitionTo(mInactiveState);
break;
case UNINITIALIZED:
case INVALID:
transitionTo(mUninitializedState);
break;
default:
return NOT_HANDLED;
}
StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
SupplicantState supState = (SupplicantState) stateChangeResult.state;
setDetailedState(WifiInfo.getDetailedStateOf(supState));
mWifiInfo.setSupplicantState(supState);
mWifiInfo.setNetworkId(stateChangeResult.networkId);
//TODO: Modify WifiMonitor to report SSID on events
//mWifiInfo.setSSID()
return HANDLED;
}
/********************************************************
* HSM states
*******************************************************/
class InitializedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
}
@Override
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_START_SCAN:
WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
break;
default:
if (DBG) Log.w(TAG, "Ignoring " + message);
break;
}
return HANDLED;
}
}
class UninitializedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
mNetworkInfo.setIsAvailable(false);
resetLoopDetection();
mPasswordKeyMayBeIncorrect = false;
}
@Override
public boolean processMessage(Message message) {
switch(message.what) {
default:
if (!handleTransition(message)) {
if (DBG) Log.w(TAG, "Ignoring " + message);
}
break;
}
return HANDLED;
}
}
class InactiveState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(false);
resetLoopDetection();
mPasswordKeyMayBeIncorrect = false;
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
}
@Override
public boolean processMessage(Message message) {
return handleTransition(message);
}
}
class DisconnectedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(true);
resetLoopDetection();
/* If a disconnect event happens after a password key failure
* event, disable the network
*/
if (mPasswordKeyMayBeIncorrect) {
Log.d(TAG, "Failed to authenticate, disabling network " +
mWifiInfo.getNetworkId());
WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
mPasswordKeyMayBeIncorrect = false;
sendSupplicantStateChangedBroadcast(stateChangeResult, true);
}
else {
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
}
}
@Override
public boolean processMessage(Message message) {
return handleTransition(message);
}
}
class ScanState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(true);
mPasswordKeyMayBeIncorrect = false;
resetLoopDetection();
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
}
@Override
public boolean processMessage(Message message) {
return handleTransition(message);
}
}
class ConnectState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START_SCAN:
WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
class HandshakeState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
final Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(true);
if (mLoopDetectIndex > message.what) {
mLoopDetectCount++;
}
if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
WifiNative.disableNetworkCommand(stateChangeResult.networkId);
mLoopDetectCount = 0;
}
mLoopDetectIndex = message.what;
mPasswordKeyMayBeIncorrect = false;
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
}
@Override
public boolean processMessage(Message message) {
return handleTransition(message);
}
}
class CompletedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(true);
mRssiPollToken++;
if (mEnableRssiPolling) {
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
POLL_RSSI_INTERVAL_MSECS);
}
resetLoopDetection();
mPasswordKeyMayBeIncorrect = false;
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
}
@Override
public boolean processMessage(Message message) {
switch(message.what) {
case ASSOCIATING:
case ASSOCIATED:
case FOUR_WAY_HANDSHAKE:
case GROUP_HANDSHAKE:
case COMPLETED:
break;
case CMD_RSSI_POLL:
if (message.arg1 == mRssiPollToken) {
// Get Info and continue polling
requestPolledInfo();
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
POLL_RSSI_INTERVAL_MSECS);
} else {
// Polling has completed
}
break;
case CMD_ENABLE_RSSI_POLL:
mRssiPollToken++;
if (mEnableRssiPolling) {
// first poll
requestPolledInfo();
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
POLL_RSSI_INTERVAL_MSECS);
}
break;
default:
return handleTransition(message);
}
return HANDLED;
}
}
class DormantState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
mNetworkInfo.setIsAvailable(true);
resetLoopDetection();
mPasswordKeyMayBeIncorrect = false;
sendSupplicantStateChangedBroadcast(stateChangeResult, false);
/* TODO: reconnect is now being handled at DHCP failure handling
* If we run into issues with staying in Dormant state, might
* need a reconnect here
*/
}
@Override
public boolean processMessage(Message message) {
return handleTransition(message);
}
}
}
private class NotificationEnabledSettingObserver extends ContentObserver {
public NotificationEnabledSettingObserver(Handler handler) {
super(handler);
}
public void register() {
ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
mNotificationEnabled = getValue();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
mNotificationEnabled = getValue();
if (!mNotificationEnabled) {
// Remove any notification that may be showing
setNotificationVisible(false, 0, true, 0);
}
resetNotificationTimer();
}
private boolean getValue() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
}
}
}