blob: 360b6429c298f33446ef07a7e1caba57679d08c9 [file] [log] [blame]
/*
* 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.DhcpInfo;
import android.net.NetworkUtils;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProperties;
import android.net.wifi.WifiConfiguration.Status;
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.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 WifiStateMachine extends HierarchicalStateMachine {
private static final String TAG = "WifiStateMachine";
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 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;
private boolean mConfigChanged = 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;
// Held during driver load and unload
private static PowerManager.WakeLock sWakeLock;
private Context mContext;
private DhcpInfo mDhcpInfo;
private WifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private SupplicantStateTracker mSupplicantStateTracker;
/* Tracks the highest priority of configured networks */
private int mLastPriority = -1;
/* Tracks if all networks need to be enabled */
private boolean mEnableAllNetworks = false;
// 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;
/* Connect to a specified network (network id
* or WifiConfiguration) This involves increasing
* the priority of the network, enabling the network
* (while disabling others) and issuing a reconnect.
* Note that CMD_RECONNECT just does a reconnect to
* an existing network. All the networks get enabled
* upon a successful connection or a failure.
*/
private static final int CMD_CONNECT_NETWORK = 90;
/* Save the specified network. This involves adding
* an enabled network (if new) and updating the
* config and issuing a save on supplicant config.
*/
private static final int CMD_SAVE_NETWORK = 91;
/* Delete the specified network. This involves
* removing the network and issuing a save on
* supplicant config.
*/
private static final int CMD_FORGET_NETWORK = 92;
/**
* 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 WifiStateMachine(Context context) {
super(TAG);
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();
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();
}
};
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();
}
/*********************************************************
* 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 setDriverStart(boolean enable) {
if (enable) {
sendMessage(CMD_START_DRIVER);
} else {
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));
}
public void connectNetwork(int netId) {
sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
}
public void connectNetwork(WifiConfiguration wifiConfig) {
sendMessage(obtainMessage(CMD_CONNECT_NETWORK, wifiConfig));
}
public void saveNetwork(WifiConfiguration wifiConfig) {
sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
}
public void forgetNetwork(int netId) {
sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
}
/**
* 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());
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 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);
mConfigChanged = true;
}
}
}
/**
* 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);
intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
if (bssid != null)
intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
mContext.sendStickyBroadcast(intent);
}
private void sendConfigChangeBroadcast() {
Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
mContext.sendBroadcast(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 sendSupplicantConfigChangedBroadcast() {
Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
mContext.sendBroadcast(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.
* @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);
}
}
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 void enableAllNetworks() {
if (mEnableAllNetworks) {
mEnableAllNetworks = false;
List<WifiConfiguration> configList = getConfiguredNetworksNative();
for (WifiConfiguration config : configList) {
if(config != null && config.status == Status.DISABLED) {
WifiNative.enableNetworkCommand(config.networkId, false);
}
}
WifiNative.saveConfigCommand();
sendSupplicantConfigChangedBroadcast();
}
}
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();
mLastPriority = 0;
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);
if (config.priority > mLastPriority) {
mLastPriority = config.priority;
}
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 CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
break;
/* Discard */
case CMD_LOAD_DRIVER:
case CMD_UNLOAD_DRIVER:
case CMD_START_SUPPLICANT:
case CMD_STOP_SUPPLICANT:
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_START_AP:
case CMD_STOP_AP:
case CMD_RECONFIGURE_IP:
case SUP_CONNECTION_EVENT:
case SUP_DISCONNECTION_EVENT:
case DRIVER_START_EVENT:
case DRIVER_STOP_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case SCAN_RESULTS_EVENT:
case SUPPLICANT_STATE_CHANGE_EVENT:
case PASSWORD_MAY_BE_INCORRECT_EVENT:
case CMD_BLACKLIST_NETWORK:
case CMD_CLEAR_BLACKLIST:
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_REQUEST_CM_WAKELOCK:
case CMD_CONNECT_NETWORK:
case CMD_SAVE_NETWORK:
case CMD_FORGET_NETWORK:
break;
default:
Log.e(TAG, "Error! unhandled message" + 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());
/* TODO: add a timeout to fail when driver load is hung.
* Similarly for driver unload.
*/
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 */
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;
WifiConfiguration config;
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);
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();
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;
config = (WifiConfiguration) syncParams.mParameter;
syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
notifyOnMsgObject(message);
sendSupplicantConfigChangedBroadcast();
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);
}
sendSupplicantConfigChangedBroadcast();
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);
}
sendSupplicantConfigChangedBroadcast();
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);
}
sendSupplicantConfigChangedBroadcast();
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;
case CMD_SAVE_NETWORK:
config = (WifiConfiguration) message.obj;
int netId = addOrUpdateNetworkNative(config);
/* enable a new network */
if (config.networkId < 0) {
WifiNative.enableNetworkCommand(netId, false);
}
WifiNative.saveConfigCommand();
sendSupplicantConfigChangedBroadcast();
break;
case CMD_FORGET_NETWORK:
WifiNative.removeNetworkCommand(message.arg1);
WifiNative.saveConfigCommand();
sendSupplicantConfigChangedBroadcast();
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);
/* If supplicant has already connected, before we could finish establishing
* the control channel connection, we miss all the supplicant events.
* Disconnect and reconnect when driver has started to ensure we receive
* all supplicant events.
*
* TODO: This is a bit unclean, ideally the supplicant should never
* connect until told to do so by the framework
*/
WifiNative.disconnectCommand();
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());
}
@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 CMD_CONNECT_NETWORK:
int netId = message.arg1;
WifiConfiguration config = (WifiConfiguration) message.obj;
if (config != null) {
netId = addOrUpdateNetworkNative(config);
}
// Reset the priority of each network at start or if it goes too high.
if (mLastPriority == -1 || mLastPriority > 1000000) {
List<WifiConfiguration> configList = getConfiguredNetworksNative();
for (WifiConfiguration conf : configList) {
if (conf.networkId != -1) {
conf.priority = 0;
addOrUpdateNetworkNative(conf);
}
}
mLastPriority = 0;
}
// Set to the highest priority and save the configuration.
config = new WifiConfiguration();
config.networkId = netId;
config.priority = ++mLastPriority;
addOrUpdateNetworkNative(config);
WifiNative.saveConfigCommand();
// Connect to network by disabling others.
WifiNative.enableNetworkCommand(netId, true);
WifiNative.reconnectCommand();
mEnableAllNetworks = true;
/* Dont send a supplicant config change broadcast here
* as it is better to not expose the temporary disabling
* of all networks
*/
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;
mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
mWifiInfo.setNetworkId(stateChangeResult.networkId);
mLastNetworkId = stateChangeResult.networkId;
enableAllNetworks();
/* 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");
enableAllNetworks();
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);
//TODO: we could also detect an IP config change
// from a DHCP renewal and send out a config change
// broadcast
if (mConfigChanged) {
sendConfigChangeBroadcast();
mConfigChanged = false;
}
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);
sendSupplicantConfigChangedBroadcast();
}
/* 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 = new UninitializedState();
private HierarchicalState mInitializedState = new InitializedState();;
private HierarchicalState mInactiveState = new InactiveState();
private HierarchicalState mDisconnectState = new DisconnectedState();
private HierarchicalState mScanState = new ScanState();
private HierarchicalState mConnectState = new ConnectState();
private HierarchicalState mHandshakeState = new HandshakeState();
private HierarchicalState mCompletedState = new CompletedState();
private HierarchicalState mDormantState = new DormantState();
public SupplicantStateTracker(Context context, Handler target) {
super(TAG, target.getLooper());
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;
}
@Override
public void exit() {
mNetworkInfo.setIsAvailable(true);
}
}
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);
}
@Override
public void exit() {
mNetworkInfo.setIsAvailable(true);
}
}
class DisconnectedState extends HierarchicalState {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
Message message = getCurrentMessage();
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
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);
sendSupplicantConfigChangedBroadcast();
}
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;
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;
if (mLoopDetectIndex > message.what) {
mLoopDetectCount++;
}
if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
WifiNative.disableNetworkCommand(stateChangeResult.networkId);
sendSupplicantConfigChangedBroadcast();
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;
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;
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);
}
}
}
}