summaryrefslogtreecommitdiffstats
path: root/wifi/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'wifi/java/android')
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl45
-rw-r--r--wifi/java/android/net/wifi/NetworkUpdateResult.java61
-rw-r--r--wifi/java/android/net/wifi/SupplicantState.java20
-rw-r--r--wifi/java/android/net/wifi/SupplicantStateTracker.java327
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java1354
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java120
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java25
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java498
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java75
-rw-r--r--wifi/java/android/net/wifi/WifiNative.java44
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java3006
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java2687
-rw-r--r--wifi/java/android/net/wifi/WpsConfiguration.aidl19
-rw-r--r--wifi/java/android/net/wifi/WpsConfiguration.java127
-rw-r--r--wifi/java/android/net/wifi/WpsResult.aidl19
-rw-r--r--wifi/java/android/net/wifi/WpsResult.java90
-rw-r--r--wifi/java/android/net/wifi/WpsStateMachine.java206
17 files changed, 6013 insertions, 2710 deletions
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 198b1e6..16a61db 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2008, 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
+ * 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
+ * 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
+ * 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.
*/
@@ -18,9 +18,12 @@ package android.net.wifi;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WpsConfiguration;
+import android.net.wifi.WpsResult;
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
+import android.os.Messenger;
import android.os.WorkSource;
/**
@@ -46,11 +49,11 @@ interface IWifiManager
List<ScanResult> getScanResults();
- boolean disconnect();
+ void disconnect();
- boolean reconnect();
+ void reconnect();
- boolean reassociate();
+ void reassociate();
WifiInfo getConnectionInfo();
@@ -58,11 +61,13 @@ interface IWifiManager
int getWifiEnabledState();
- int getNumAllowedChannels();
+ void setCountryCode(String country, boolean persist);
- boolean setNumAllowedChannels(int numChannels, boolean persist);
+ void setFrequencyBand(int band, boolean persist);
- int[] getValidChannelCounts();
+ int getFrequencyBand();
+
+ boolean isDualBandSupported();
boolean saveConfiguration();
@@ -89,5 +94,17 @@ interface IWifiManager
WifiConfiguration getWifiApConfiguration();
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+ void startWifi();
+
+ void stopWifi();
+
+ void addToBlacklist(String bssid);
+
+ void clearBlacklist();
+
+ Messenger getMessenger();
+
+ String getConfigFile();
}
diff --git a/wifi/java/android/net/wifi/NetworkUpdateResult.java b/wifi/java/android/net/wifi/NetworkUpdateResult.java
new file mode 100644
index 0000000..6b7b68b
--- /dev/null
+++ b/wifi/java/android/net/wifi/NetworkUpdateResult.java
@@ -0,0 +1,61 @@
+/*
+ * 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.WifiConfiguration.INVALID_NETWORK_ID;
+
+class NetworkUpdateResult {
+ int netId;
+ boolean ipChanged;
+ boolean proxyChanged;
+
+ public NetworkUpdateResult(int id) {
+ netId = id;
+ ipChanged = false;
+ proxyChanged = false;
+ }
+
+ public NetworkUpdateResult(boolean ip, boolean proxy) {
+ netId = INVALID_NETWORK_ID;
+ ipChanged = ip;
+ proxyChanged = proxy;
+ }
+
+ public void setNetworkId(int id) {
+ netId = id;
+ }
+
+ public int getNetworkId() {
+ return netId;
+ }
+
+ public void setIpChanged(boolean ip) {
+ ipChanged = ip;
+ }
+
+ public boolean hasIpChanged() {
+ return ipChanged;
+ }
+
+ public void setProxyChanged(boolean proxy) {
+ proxyChanged = proxy;
+ }
+
+ public boolean hasProxyChanged() {
+ return proxyChanged;
+ }
+}
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 169b2d6..6b79210 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -152,6 +152,26 @@ public enum SupplicantState implements Parcelable {
return state != UNINITIALIZED && state != INVALID;
}
+ static boolean isConnecting(SupplicantState state) {
+ switch(state) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ return true;
+ case DISCONNECTED:
+ case INACTIVE:
+ case SCANNING:
+ case DORMANT:
+ case UNINITIALIZED:
+ case INVALID:
+ return false;
+ default:
+ throw new IllegalArgumentException("Unknown supplicant state");
+ }
+ }
+
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
new file mode 100644
index 0000000..3cde949
--- /dev/null
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -0,0 +1,327 @@
+/*
+ * 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 com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import android.net.wifi.WifiStateMachine.StateChangeResult;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Tracks the state changes in supplicant and provides functionality
+ * that is based on these state changes:
+ * - detect a failed WPA handshake that loops indefinitely
+ * - authentication failure handling
+ */
+class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private static final String TAG = "SupplicantStateTracker";
+ private static final boolean DBG = false;
+
+ private WifiStateMachine mWifiStateMachine;
+ private int mAuthenticationFailuresCount = 0;
+ /* Indicates authentication failure in supplicant broadcast.
+ * TODO: enhance auth failure reporting to include notification
+ * for all type of failures: EAP, WPS & WPA networks */
+ private boolean mAuthFailureInSupplicantBroadcast = false;
+
+ /* Maximum retries on a authentication failure notification */
+ private static final int MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
+
+ /* Tracks if networks have been disabled during a connection */
+ private boolean mNetworksDisabledDuringConnect = false;
+
+ private Context mContext;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mDefaultState = new DefaultState();
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, WifiStateMachine wsm, Handler target) {
+ super(TAG, target.getLooper());
+
+ mContext = context;
+ mWifiStateMachine = wsm;
+ addState(mDefaultState);
+ addState(mUninitializedState, mDefaultState);
+ addState(mInactiveState, mDefaultState);
+ addState(mDisconnectState, mDefaultState);
+ addState(mScanState, mDefaultState);
+ addState(mHandshakeState, mDefaultState);
+ addState(mCompletedState, mDefaultState);
+ addState(mDormantState, mDefaultState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ private void handleNetworkConnectionFailure(int netId) {
+ /* If other networks disabled during connection, enable them */
+ if (mNetworksDisabledDuringConnect) {
+ WifiConfigStore.enableAllNetworks();
+ mNetworksDisabledDuringConnect = false;
+ }
+ /* Disable failed network */
+ WifiConfigStore.disableNetwork(netId);
+ }
+
+ private void transitionOnSupplicantStateChange(StateChangeResult stateChangeResult) {
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+
+ if (DBG) Log.d(TAG, "Supplicant state: " + supState.toString() + "\n");
+
+ switch (supState) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ 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:
+ Log.e(TAG, "Unknown supplicant state " + supState);
+ break;
+ }
+ }
+
+ private void sendSupplicantStateChangedBroadcast(SupplicantState state, 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) state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState 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 WifiStateMachine.AUTHENTICATION_FAILURE_EVENT:
+ mAuthenticationFailuresCount++;
+ mAuthFailureInSupplicantBroadcast = true;
+ break;
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = stateChangeResult.state;
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
+ mAuthFailureInSupplicantBroadcast = false;
+ transitionOnSupplicantStateChange(stateChangeResult);
+ break;
+ case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
+ transitionTo(mUninitializedState);
+ break;
+ case WifiStateMachine.CMD_CONNECT_NETWORK:
+ mNetworksDisabledDuringConnect = true;
+ break;
+ default:
+ Log.e(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ /*
+ * This indicates that the supplicant state as seen
+ * by the framework is not initialized yet. We are
+ * in this state right after establishing a control
+ * channel connection before any supplicant events
+ * or after we have lost the control channel
+ * connection to the supplicant
+ */
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ /* If a disconnect event happens after authentication failure
+ * exceeds maximum retries, disable the network
+ */
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mAuthenticationFailuresCount >= MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ stateChangeResult.networkId);
+ handleNetworkConnectionFailure(stateChangeResult.networkId);
+ mAuthenticationFailuresCount = 0;
+ }
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ /**
+ * 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;
+ private int mLoopDetectCount;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mLoopDetectIndex = 0;
+ mLoopDetectCount = 0;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = stateChangeResult.state;
+ if (state == SupplicantState.ASSOCIATING ||
+ state == SupplicantState.ASSOCIATED ||
+ state == SupplicantState.FOUR_WAY_HANDSHAKE ||
+ state == SupplicantState.GROUP_HANDSHAKE) {
+ if (mLoopDetectIndex > state.ordinal()) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ Log.d(TAG, "Supplicant loop detected, disabling network " +
+ stateChangeResult.networkId);
+ handleNetworkConnectionFailure(stateChangeResult.networkId);
+ }
+ mLoopDetectIndex = state.ordinal();
+ sendSupplicantStateChangedBroadcast(state,
+ mAuthFailureInSupplicantBroadcast);
+ } else {
+ //Have the DefaultState handle the transition
+ return NOT_HANDLED;
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ /* Reset authentication failure count */
+ mAuthenticationFailuresCount = 0;
+ if (mNetworksDisabledDuringConnect) {
+ WifiConfigStore.enableAllNetworks();
+ mNetworksDisabledDuringConnect = false;
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = stateChangeResult.state;
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
+ /* Ignore a re-auth in completed state */
+ if (state == SupplicantState.ASSOCIATING ||
+ state == SupplicantState.ASSOCIATED ||
+ state == SupplicantState.FOUR_WAY_HANDSHAKE ||
+ state == SupplicantState.GROUP_HANDSHAKE ||
+ state == SupplicantState.COMPLETED) {
+ break;
+ }
+ transitionOnSupplicantStateChange(stateChangeResult);
+ break;
+ case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
+ sendSupplicantStateChangedBroadcast(SupplicantState.DISCONNECTED, false);
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ //TODO: remove after getting rid of the state in supplicant
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
new file mode 100644
index 0000000..6455d84
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -0,0 +1,1354 @@
+/*
+ * 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 android.content.Context;
+import android.content.Intent;
+import android.net.DhcpInfoInternal;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.ProxyProperties;
+import android.net.wifi.WifiConfiguration.IpAssignment;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.wifi.WifiConfiguration.Status;
+import android.net.wifi.NetworkUpdateResult;
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class provides the API to manage configured
+ * wifi networks. The API is not thread safe is being
+ * used only from WifiStateMachine.
+ *
+ * It deals with the following
+ * - Add/update/remove a WifiConfiguration
+ * The configuration contains two types of information.
+ * = IP and proxy configuration that is handled by WifiConfigStore and
+ * is saved to disk on any change.
+ *
+ * The format of configuration file is as follows:
+ * <version>
+ * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
+ * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
+ * ..
+ *
+ * (key, value) pairs for a given network are grouped together and can
+ * be in any order. A EOS at the end of a set of (key, value) pairs
+ * indicates that the next set of (key, value) pairs are for a new
+ * network. A network is identified by a unique ID_KEY. If there is no
+ * ID_KEY in the (key, value) pairs, the data is discarded.
+ *
+ * An invalid version on read would result in discarding the contents of
+ * the file. On the next write, the latest version is written to file.
+ *
+ * Any failures during read or write to the configuration file are ignored
+ * without reporting to the user since the likelihood of these errors are
+ * low and the impact on connectivity is low.
+ *
+ * = SSID & security details that is pushed to the supplicant.
+ * supplicant saves these details to the disk on calling
+ * saveConfigCommand().
+ *
+ * We have two kinds of APIs exposed:
+ * > public API calls that provide fine grained control
+ * - enableNetwork, disableNetwork, addOrUpdateNetwork(),
+ * removeNetwork(). For these calls, the config is not persisted
+ * to the disk. (TODO: deprecate these calls in WifiManager)
+ * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
+ * These calls persist the supplicant config to disk.
+ *
+ * - Maintain a list of configured networks for quick access
+ *
+ */
+class WifiConfigStore {
+
+ private static Context sContext;
+ private static final String TAG = "WifiConfigStore";
+
+ /* configured networks with network id as the key */
+ private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
+ new HashMap<Integer, WifiConfiguration>();
+
+ /* A network id is a unique identifier for a network configured in the
+ * supplicant. Network ids are generated when the supplicant reads
+ * the configuration file at start and can thus change for networks.
+ * We store the IP configuration for networks along with a unique id
+ * that is generated from SSID and security type of the network. A mapping
+ * from the generated unique id to network id of the network is needed to
+ * map supplicant config to IP configuration. */
+ private static HashMap<Integer, Integer> sNetworkIds =
+ new HashMap<Integer, Integer>();
+
+ /* Tracks the highest priority of configured networks */
+ private static int sLastPriority = -1;
+
+ private static final String ipConfigFile = Environment.getDataDirectory() +
+ "/misc/wifi/ipconfig.txt";
+
+ private static final int IPCONFIG_FILE_VERSION = 1;
+
+ /* IP and proxy configuration keys */
+ private static final String ID_KEY = "id";
+ private static final String IP_ASSIGNMENT_KEY = "ipAssignment";
+ private static final String LINK_ADDRESS_KEY = "linkAddress";
+ private static final String GATEWAY_KEY = "gateway";
+ private static final String DNS_KEY = "dns";
+ private static final String PROXY_SETTINGS_KEY = "proxySettings";
+ private static final String PROXY_HOST_KEY = "proxyHost";
+ private static final String PROXY_PORT_KEY = "proxyPort";
+ private static final String EXCLUSION_LIST_KEY = "exclusionList";
+ private static final String EOS = "eos";
+
+ /**
+ * Initialize context, fetch the list of configured networks
+ * and enable all stored networks in supplicant.
+ */
+ static void initialize(Context context) {
+ Log.d(TAG, "Loading config and enabling all networks");
+ sContext = context;
+ loadConfiguredNetworks();
+ enableAllNetworks();
+ }
+
+ /**
+ * Fetch the list of currently configured networks
+ * @return List of networks
+ */
+ static List<WifiConfiguration> getConfiguredNetworks() {
+ List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ networks.add(new WifiConfiguration(config));
+ }
+ }
+ return networks;
+ }
+
+ /**
+ * enable all networks and save config. This will be a no-op if the list
+ * of configured networks indicates all networks as being enabled
+ */
+ static void enableAllNetworks() {
+ boolean networkEnabledStateChanged = false;
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if(config != null && config.status == Status.DISABLED) {
+ if(WifiNative.enableNetworkCommand(config.networkId, false)) {
+ networkEnabledStateChanged = true;
+ config.status = Status.ENABLED;
+ } else {
+ Log.e(TAG, "Enable network failed on " + config.networkId);
+ }
+ }
+ }
+ }
+
+ if (networkEnabledStateChanged) {
+ WifiNative.saveConfigCommand();
+ sendConfiguredNetworksChangedBroadcast();
+ }
+ }
+
+ /**
+ * Selects the specified network config for connection. This involves
+ * addition/update of the specified config, updating the priority of
+ * all the networks and enabling the given network while disabling others.
+ *
+ * Selecting a network will leave the other networks disabled and
+ * a call to enableAllNetworks() needs to be issued upon a connection
+ * or a failure event from supplicant
+ *
+ * @param config The configuration details in WifiConfiguration
+ */
+ static void selectNetwork(WifiConfiguration config) {
+ if (config != null) {
+ NetworkUpdateResult result = addOrUpdateNetworkNative(config);
+ int netId = result.getNetworkId();
+ if (netId != INVALID_NETWORK_ID) {
+ selectNetwork(netId);
+ } else {
+ Log.e(TAG, "Failed to update network " + config);
+ }
+ }
+ }
+
+ /**
+ * Selects the specified network for connection. This involves
+ * updating the priority of all the networks and enabling the given
+ * network while disabling others.
+ *
+ * Selecting a network will leave the other networks disabled and
+ * a call to enableAllNetworks() needs to be issued upon a connection
+ * or a failure event from supplicant
+ *
+ * @param netId network to select for connection
+ */
+ static void selectNetwork(int netId) {
+ // Reset the priority of each network at start or if it goes too high.
+ if (sLastPriority == -1 || sLastPriority > 1000000) {
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if (config.networkId != INVALID_NETWORK_ID) {
+ config.priority = 0;
+ addOrUpdateNetworkNative(config);
+ }
+ }
+ }
+ sLastPriority = 0;
+ }
+
+ // Set to the highest priority and save the configuration.
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = netId;
+ config.priority = ++sLastPriority;
+
+ addOrUpdateNetworkNative(config);
+ WifiNative.saveConfigCommand();
+
+ /* Enable the given network while disabling all other networks */
+ enableNetworkWithoutBroadcast(netId, true);
+
+ /* Avoid saving the config & sending a broadcast to prevent settings
+ * from displaying a disabled list of networks */
+ }
+
+ /**
+ * Add/update the specified configuration and save config
+ *
+ * @param config WifiConfiguration to be saved
+ */
+ static NetworkUpdateResult saveNetwork(WifiConfiguration config) {
+ boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
+ NetworkUpdateResult result = addOrUpdateNetworkNative(config);
+ int netId = result.getNetworkId();
+ /* enable a new network */
+ if (newNetwork && netId != INVALID_NETWORK_ID) {
+ WifiNative.enableNetworkCommand(netId, false);
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.get(netId).status = Status.ENABLED;
+ }
+ }
+ WifiNative.saveConfigCommand();
+ sendConfiguredNetworksChangedBroadcast();
+ return result;
+ }
+
+ /**
+ * Forget the specified network and save config
+ *
+ * @param netId network to forget
+ */
+ static void forgetNetwork(int netId) {
+ if (WifiNative.removeNetworkCommand(netId)) {
+ WifiNative.saveConfigCommand();
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.remove(netId);
+ }
+ writeIpAndProxyConfigurations();
+ sendConfiguredNetworksChangedBroadcast();
+ } else {
+ Log.e(TAG, "Failed to remove network " + netId);
+ }
+ }
+
+ /**
+ * Add/update a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful saveNetwork() is used by the
+ * state machine
+ *
+ * @param config wifi configuration to add/update
+ */
+ static int addOrUpdateNetwork(WifiConfiguration config) {
+ NetworkUpdateResult result = addOrUpdateNetworkNative(config);
+ sendConfiguredNetworksChangedBroadcast();
+ return result.getNetworkId();
+ }
+
+ /**
+ * Remove a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful forgetNetwork() is used by the
+ * state machine for network removal
+ *
+ * @param netId network to be removed
+ */
+ static boolean removeNetwork(int netId) {
+ boolean ret = WifiNative.removeNetworkCommand(netId);
+ synchronized (sConfiguredNetworks) {
+ if (ret) sConfiguredNetworks.remove(netId);
+ }
+ sendConfiguredNetworksChangedBroadcast();
+ return ret;
+ }
+
+ /**
+ * Enable a network. Note that there is no saveConfig operation.
+ * This function is retained for compatibility with the public
+ * API. The more powerful selectNetwork()/saveNetwork() is used by the
+ * state machine for connecting to a network
+ *
+ * @param netId network to be removed
+ */
+ static boolean enableNetwork(int netId, boolean disableOthers) {
+ boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
+ sendConfiguredNetworksChangedBroadcast();
+ return ret;
+ }
+
+ static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
+ boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
+
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) config.status = Status.ENABLED;
+ }
+
+ if (disableOthers) {
+ markAllNetworksDisabledExcept(netId);
+ }
+ return ret;
+ }
+
+ /**
+ * Disable a network. Note that there is no saveConfig operation.
+ * @param netId network to be disabled
+ */
+ static boolean disableNetwork(int netId) {
+ boolean ret = WifiNative.disableNetworkCommand(netId);
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) config.status = Status.DISABLED;
+ }
+ sendConfiguredNetworksChangedBroadcast();
+ return ret;
+ }
+
+ /**
+ * Save the configured networks in supplicant to disk
+ */
+ static boolean saveConfig() {
+ return WifiNative.saveConfigCommand();
+ }
+
+ /**
+ * Start WPS pin method configuration with pin obtained
+ * from the access point
+ */
+ static WpsResult startWpsWithPinFromAccessPoint(WpsConfiguration config) {
+ WpsResult result = new WpsResult();
+ if (WifiNative.startWpsWithPinFromAccessPointCommand(config.BSSID, config.pin)) {
+ /* WPS leaves all networks disabled */
+ markAllNetworksDisabled();
+ result.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS pin method configuration");
+ result.status = WpsResult.Status.FAILURE;
+ }
+ return result;
+ }
+
+ /**
+ * Start WPS pin method configuration with pin obtained
+ * from the device
+ * @return WpsResult indicating status and pin
+ */
+ static WpsResult startWpsWithPinFromDevice(WpsConfiguration config) {
+ WpsResult result = new WpsResult();
+ result.pin = WifiNative.startWpsWithPinFromDeviceCommand(config.BSSID);
+ /* WPS leaves all networks disabled */
+ if (!TextUtils.isEmpty(result.pin)) {
+ markAllNetworksDisabled();
+ result.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS pin method configuration");
+ result.status = WpsResult.Status.FAILURE;
+ }
+ return result;
+ }
+
+ /**
+ * Start WPS push button configuration
+ */
+ static WpsResult startWpsPbc(WpsConfiguration config) {
+ WpsResult result = new WpsResult();
+ if (WifiNative.startWpsPbcCommand(config.BSSID)) {
+ /* WPS leaves all networks disabled */
+ markAllNetworksDisabled();
+ result.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS push button configuration");
+ result.status = WpsResult.Status.FAILURE;
+ }
+ return result;
+ }
+
+ /**
+ * Fetch the link properties for a given network id
+ */
+ static LinkProperties getLinkProperties(int netId) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) return new LinkProperties(config.linkProperties);
+ }
+ return null;
+ }
+
+ /**
+ * get IP configuration for a given network id
+ * TODO: We cannot handle IPv6 addresses for configuration
+ * right now until NetworkUtils is fixed. When we do
+ * that, we should remove handling DhcpInfo and move
+ * to using LinkProperties
+ */
+ static DhcpInfoInternal getIpConfiguration(int netId) {
+ DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+ LinkProperties linkProperties = getLinkProperties(netId);
+
+ if (linkProperties != null) {
+ Iterator<LinkAddress> iter = linkProperties.getLinkAddresses().iterator();
+ if (iter.hasNext()) {
+ LinkAddress linkAddress = iter.next();
+ dhcpInfoInternal.ipAddress = linkAddress.getAddress().getHostAddress();
+ Iterator<InetAddress>gateways = linkProperties.getGateways().iterator();
+ if (gateways.hasNext()) {
+ dhcpInfoInternal.gateway = gateways.next().getHostAddress();
+ }
+ dhcpInfoInternal.prefixLength = linkAddress.getNetworkPrefixLength();
+ Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
+ dhcpInfoInternal.dns1 = dnsIterator.next().getHostAddress();
+ if (dnsIterator.hasNext()) {
+ dhcpInfoInternal.dns2 = dnsIterator.next().getHostAddress();
+ }
+ }
+ }
+ return dhcpInfoInternal;
+ }
+
+ /**
+ * set IP configuration for a given network id
+ */
+ static void setIpConfiguration(int netId, DhcpInfoInternal dhcpInfo) {
+ LinkProperties linkProperties = dhcpInfo.makeLinkProperties();
+
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) {
+ // add old proxy details
+ if(config.linkProperties != null) {
+ linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
+ }
+ config.linkProperties = linkProperties;
+ }
+ }
+ }
+
+
+ /**
+ * Fetch the proxy properties for a given network id
+ */
+ static ProxyProperties getProxyProperties(int netId) {
+ LinkProperties linkProperties = getLinkProperties(netId);
+ if (linkProperties != null) {
+ return new ProxyProperties(linkProperties.getHttpProxy());
+ }
+ return null;
+ }
+
+ /**
+ * Return if the specified network is using static IP
+ */
+ static boolean isUsingStaticIp(int netId) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void sendConfiguredNetworksChangedBroadcast() {
+ Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ sContext.sendBroadcast(intent);
+ }
+
+ static void loadConfiguredNetworks() {
+ String listStr = WifiNative.listNetworksCommand();
+ sLastPriority = 0;
+
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.clear();
+ sNetworkIds.clear();
+
+ if (listStr == null)
+ return;
+
+ 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 > sLastPriority) {
+ sLastPriority = config.priority;
+ }
+ sConfiguredNetworks.put(config.networkId, config);
+ sNetworkIds.put(configKey(config), config.networkId);
+ }
+ }
+ readIpAndProxyConfigurations();
+ sendConfiguredNetworksChangedBroadcast();
+ }
+
+ static void updateIpAndProxyFromWpsConfig(int netId, WpsConfiguration wpsConfig) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(netId);
+ if (config != null) {
+ config.ipAssignment = wpsConfig.ipAssignment;
+ config.proxySettings = wpsConfig.proxySettings;
+ config.linkProperties = wpsConfig.linkProperties;
+ writeIpAndProxyConfigurations();
+ }
+ }
+ }
+
+ /* Mark all networks except specified netId as disabled */
+ private static void markAllNetworksDisabledExcept(int netId) {
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ if(config != null && config.networkId != netId) {
+ config.status = Status.DISABLED;
+ }
+ }
+ }
+ }
+
+ private static void markAllNetworksDisabled() {
+ markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
+ }
+
+ private static void writeIpAndProxyConfigurations() {
+
+ DataOutputStream out = null;
+ try {
+ out = new DataOutputStream(new BufferedOutputStream(
+ new FileOutputStream(ipConfigFile)));
+
+ out.writeInt(IPCONFIG_FILE_VERSION);
+
+ synchronized (sConfiguredNetworks) {
+ for(WifiConfiguration config : sConfiguredNetworks.values()) {
+ boolean writeToFile = false;
+
+ try {
+ LinkProperties linkProperties = config.linkProperties;
+ switch (config.ipAssignment) {
+ case STATIC:
+ out.writeUTF(IP_ASSIGNMENT_KEY);
+ out.writeUTF(config.ipAssignment.toString());
+ for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
+ out.writeUTF(LINK_ADDRESS_KEY);
+ out.writeUTF(linkAddr.getAddress().getHostAddress());
+ out.writeInt(linkAddr.getNetworkPrefixLength());
+ }
+ for (InetAddress gateway : linkProperties.getGateways()) {
+ out.writeUTF(GATEWAY_KEY);
+ out.writeUTF(gateway.getHostAddress());
+ }
+ for (InetAddress inetAddr : linkProperties.getDnses()) {
+ out.writeUTF(DNS_KEY);
+ out.writeUTF(inetAddr.getHostAddress());
+ }
+ writeToFile = true;
+ break;
+ case DHCP:
+ out.writeUTF(IP_ASSIGNMENT_KEY);
+ out.writeUTF(config.ipAssignment.toString());
+ writeToFile = true;
+ break;
+ case UNASSIGNED:
+ /* Ignore */
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid ip assignment while writing");
+ break;
+ }
+
+ switch (config.proxySettings) {
+ case STATIC:
+ ProxyProperties proxyProperties = linkProperties.getHttpProxy();
+ String exclusionList = proxyProperties.getExclusionList();
+ out.writeUTF(PROXY_SETTINGS_KEY);
+ out.writeUTF(config.proxySettings.toString());
+ out.writeUTF(PROXY_HOST_KEY);
+ out.writeUTF(proxyProperties.getHost());
+ out.writeUTF(PROXY_PORT_KEY);
+ out.writeInt(proxyProperties.getPort());
+ out.writeUTF(EXCLUSION_LIST_KEY);
+ out.writeUTF(exclusionList);
+ writeToFile = true;
+ break;
+ case NONE:
+ out.writeUTF(PROXY_SETTINGS_KEY);
+ out.writeUTF(config.proxySettings.toString());
+ writeToFile = true;
+ break;
+ case UNASSIGNED:
+ /* Ignore */
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid proxy settings while writing");
+ break;
+ }
+ if (writeToFile) {
+ out.writeUTF(ID_KEY);
+ out.writeInt(configKey(config));
+ }
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Failure in writing " + config.linkProperties + e);
+ }
+ out.writeUTF(EOS);
+ }
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing data file");
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (Exception e) {}
+ }
+ }
+ }
+
+ private static void readIpAndProxyConfigurations() {
+
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(new FileInputStream(
+ ipConfigFile)));
+
+ if (in.readInt() != IPCONFIG_FILE_VERSION) {
+ Log.e(TAG, "Bad version on IP configuration file, ignore read");
+ return;
+ }
+
+ while (true) {
+ int id = -1;
+ IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
+ ProxySettings proxySettings = ProxySettings.UNASSIGNED;
+ LinkProperties linkProperties = new LinkProperties();
+ String proxyHost = null;
+ int proxyPort = -1;
+ String exclusionList = null;
+ String key;
+
+ do {
+ key = in.readUTF();
+ try {
+ if (key.equals(ID_KEY)) {
+ id = in.readInt();
+ } else if (key.equals(IP_ASSIGNMENT_KEY)) {
+ ipAssignment = IpAssignment.valueOf(in.readUTF());
+ } else if (key.equals(LINK_ADDRESS_KEY)) {
+ LinkAddress linkAddr = new LinkAddress(
+ NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
+ linkProperties.addLinkAddress(linkAddr);
+ } else if (key.equals(GATEWAY_KEY)) {
+ linkProperties.addGateway(
+ NetworkUtils.numericToInetAddress(in.readUTF()));
+ } else if (key.equals(DNS_KEY)) {
+ linkProperties.addDns(
+ NetworkUtils.numericToInetAddress(in.readUTF()));
+ } else if (key.equals(PROXY_SETTINGS_KEY)) {
+ proxySettings = ProxySettings.valueOf(in.readUTF());
+ } else if (key.equals(PROXY_HOST_KEY)) {
+ proxyHost = in.readUTF();
+ } else if (key.equals(PROXY_PORT_KEY)) {
+ proxyPort = in.readInt();
+ } else if (key.equals(EXCLUSION_LIST_KEY)) {
+ exclusionList = in.readUTF();
+ } else if (key.equals(EOS)) {
+ break;
+ } else {
+ Log.e(TAG, "Ignore unknown key " + key + "while reading");
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Ignore invalid address while reading" + e);
+ }
+ } while (true);
+
+ if (id != -1) {
+ synchronized (sConfiguredNetworks) {
+ WifiConfiguration config = sConfiguredNetworks.get(
+ sNetworkIds.get(id));
+
+ if (config == null) {
+ Log.e(TAG, "configuration found for missing network, ignored");
+ } else {
+ config.linkProperties = linkProperties;
+ switch (ipAssignment) {
+ case STATIC:
+ case DHCP:
+ config.ipAssignment = ipAssignment;
+ break;
+ case UNASSIGNED:
+ //Ignore
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid ip assignment while reading");
+ break;
+ }
+
+ switch (proxySettings) {
+ case STATIC:
+ config.proxySettings = proxySettings;
+ ProxyProperties proxyProperties =
+ new ProxyProperties(proxyHost, proxyPort, exclusionList);
+ linkProperties.setHttpProxy(proxyProperties);
+ break;
+ case NONE:
+ config.proxySettings = proxySettings;
+ break;
+ case UNASSIGNED:
+ //Ignore
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid proxy settings while reading");
+ break;
+ }
+ }
+ }
+ } else {
+ Log.e(TAG, "Missing id while parsing configuration");
+ }
+ }
+ } catch (EOFException ignore) {
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing configuration" + e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (Exception e) {}
+ }
+ }
+ }
+
+ private static NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config) {
+ /*
+ * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
+ * network configuration. Otherwise, the networkId should
+ * refer to an existing configuration.
+ */
+ int netId = config.networkId;
+ boolean updateFailed = true;
+ // networkId of INVALID_NETWORK_ID means we want to create a new network
+ boolean newNetwork = (netId == INVALID_NETWORK_ID);
+
+ if (newNetwork) {
+ netId = WifiNative.addNetworkCommand();
+ if (netId < 0) {
+ Log.e(TAG, "Failed to add a network!");
+ return new NetworkUpdateResult(INVALID_NETWORK_ID);
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+ updateFailed = false;
+ }
+
+ if (updateFailed) {
+ if (newNetwork) {
+ WifiNative.removeNetworkCommand(netId);
+ Log.d(TAG,
+ "Failed to set a network variable, removed network: "
+ + netId);
+ }
+ return new NetworkUpdateResult(INVALID_NETWORK_ID);
+ }
+
+ /* An update of the network variables requires reading them
+ * back from the supplicant to update sConfiguredNetworks.
+ * This is because some of the variables (SSID, wep keys &
+ * passphrases) reflect different values when read back than
+ * when written. For example, wep key is stored as * irrespective
+ * of the value sent to the supplicant
+ */
+ WifiConfiguration sConfig;
+ synchronized (sConfiguredNetworks) {
+ sConfig = sConfiguredNetworks.get(netId);
+ }
+ if (sConfig == null) {
+ sConfig = new WifiConfiguration();
+ sConfig.networkId = netId;
+ synchronized (sConfiguredNetworks) {
+ sConfiguredNetworks.put(netId, sConfig);
+ }
+ }
+ readNetworkVariables(sConfig);
+
+ NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(sConfig, config);
+ result.setNetworkId(netId);
+ return result;
+ }
+
+ /* Compare current and new configuration and write to file on change */
+ private static NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
+ WifiConfiguration currentConfig,
+ WifiConfiguration newConfig) {
+ boolean ipChanged = false;
+ boolean proxyChanged = false;
+ LinkProperties linkProperties = new LinkProperties();
+
+ switch (newConfig.ipAssignment) {
+ case STATIC:
+ Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
+ .getLinkAddresses();
+ Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
+ .getLinkAddresses();
+ Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
+ Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
+ Collection<InetAddress> currentGateways =
+ currentConfig.linkProperties.getGateways();
+ Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways();
+
+ boolean linkAddressesDiffer =
+ (currentLinkAddresses.size() != newLinkAddresses.size()) ||
+ !currentLinkAddresses.containsAll(newLinkAddresses);
+ boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
+ !currentDnses.containsAll(newDnses);
+ boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) ||
+ !currentGateways.containsAll(newGateways);
+
+ if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
+ linkAddressesDiffer ||
+ dnsesDiffer ||
+ gatewaysDiffer) {
+ ipChanged = true;
+ }
+ break;
+ case DHCP:
+ if (currentConfig.ipAssignment != newConfig.ipAssignment) {
+ ipChanged = true;
+ }
+ break;
+ case UNASSIGNED:
+ /* Ignore */
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid ip assignment during write");
+ break;
+ }
+
+ switch (newConfig.proxySettings) {
+ case STATIC:
+ ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
+ ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
+
+ if (newHttpProxy != null) {
+ proxyChanged = !newHttpProxy.equals(currentHttpProxy);
+ } else {
+ proxyChanged = (currentHttpProxy != null);
+ }
+ break;
+ case NONE:
+ if (currentConfig.proxySettings != newConfig.proxySettings) {
+ proxyChanged = true;
+ }
+ break;
+ case UNASSIGNED:
+ /* Ignore */
+ break;
+ default:
+ Log.e(TAG, "Ignore invalid proxy configuration during write");
+ break;
+ }
+
+ if (!ipChanged) {
+ addIpSettingsFromConfig(linkProperties, currentConfig);
+ } else {
+ currentConfig.ipAssignment = newConfig.ipAssignment;
+ addIpSettingsFromConfig(linkProperties, newConfig);
+ Log.d(TAG, "IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
+ linkProperties.toString());
+ }
+
+
+ if (!proxyChanged) {
+ linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
+ } else {
+ currentConfig.proxySettings = newConfig.proxySettings;
+ linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
+ Log.d(TAG, "proxy changed SSID = " + currentConfig.SSID);
+ if (linkProperties.getHttpProxy() != null) {
+ Log.d(TAG, " proxyProperties: " + linkProperties.getHttpProxy().toString());
+ }
+ }
+
+ if (ipChanged || proxyChanged) {
+ currentConfig.linkProperties = linkProperties;
+ writeIpAndProxyConfigurations();
+ sendConfiguredNetworksChangedBroadcast();
+ }
+ return new NetworkUpdateResult(ipChanged, proxyChanged);
+ }
+
+ private static void addIpSettingsFromConfig(LinkProperties linkProperties,
+ WifiConfiguration config) {
+ for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
+ linkProperties.addLinkAddress(linkAddr);
+ }
+ for (InetAddress gateway : config.linkProperties.getGateways()) {
+ linkProperties.addGateway(gateway);
+ }
+ for (InetAddress dns : config.linkProperties.getDnses()) {
+ linkProperties.addDns(dns);
+ }
+ }
+
+ /**
+ * 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 static 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 = 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);
+ }
+ }
+ }
+
+ 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;
+ }
+
+ /* Returns a unique for a given configuration */
+ private static int configKey(WifiConfiguration config) {
+ String key;
+
+ if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+ } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+ config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ } else if (config.wepKeys[0] != null) {
+ key = config.SSID + "WEP";
+ } else {
+ key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
+ }
+
+ return key.hashCode();
+ }
+
+ static String dump() {
+ StringBuffer sb = new StringBuffer();
+ String LS = System.getProperty("line.separator");
+ sb.append("sLastPriority ").append(sLastPriority).append(LS);
+ sb.append("Configured networks ").append(LS);
+ for (WifiConfiguration conf : getConfiguredNetworks()) {
+ sb.append(conf).append(LS);
+ }
+ return sb.toString();
+ }
+
+ public static String getConfigFile() {
+ return ipConfigFile;
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 01bc919..28a5bc6 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.net.LinkProperties;
import android.os.Parcelable;
import android.os.Parcel;
@@ -42,6 +43,8 @@ public class WifiConfiguration implements Parcelable {
public static final String priorityVarName = "priority";
/** {@hide} */
public static final String hiddenSSIDVarName = "scan_ssid";
+ /** {@hide} */
+ public static final int INVALID_NETWORK_ID = -1;
/** {@hide} */
public class EnterpriseField {
@@ -104,9 +107,16 @@ public class WifiConfiguration implements Parcelable {
* generated WEP keys. */
public static final int IEEE8021X = 3;
+ /** WPA2 pre-shared key for use with soft access point
+ * (requires {@code preSharedKey} to be specified).
+ * @hide
+ */
+ public static final int WPA2_PSK = 4;
+
public static final String varName = "key_mgmt";
- public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X" };
+ public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X",
+ "WPA2_PSK" };
}
/**
@@ -216,7 +226,7 @@ public class WifiConfiguration implements Parcelable {
/**
* The network's SSID. Can either be an ASCII string,
* which must be enclosed in double quotation marks
- * (e.g., {@code &quot;MyNetwork&quot;}, or a string of
+ * (e.g., {@code "MyNetwork"}, or a string of
* hex digits,which are not enclosed in quotes
* (e.g., {@code 01a243f405}).
*/
@@ -239,7 +249,7 @@ public class WifiConfiguration implements Parcelable {
public String preSharedKey;
/**
* Up to four WEP keys. Either an ASCII string enclosed in double
- * quotation marks (e.g., {@code &quot;abcdef&quot;} or a string
+ * quotation marks (e.g., {@code "abcdef"} or a string
* of hex digits (e.g., {@code 0102030405}).
* <p/>
* When the value of one of these keys is read, the actual key is
@@ -294,9 +304,49 @@ public class WifiConfiguration implements Parcelable {
*/
public BitSet allowedGroupCiphers;
+ /**
+ * @hide
+ */
+ public enum IpAssignment {
+ /* Use statically configured IP settings. Configuration can be accessed
+ * with linkProperties */
+ STATIC,
+ /* Use dynamically configured IP settigns */
+ DHCP,
+ /* no IP details are assigned, this is used to indicate
+ * that any existing IP settings should be retained */
+ UNASSIGNED
+ }
+ /**
+ * @hide
+ */
+ public IpAssignment ipAssignment;
+
+ /**
+ * @hide
+ */
+ public enum ProxySettings {
+ /* No proxy is to be used. Any existing proxy settings
+ * should be cleared. */
+ NONE,
+ /* Use statically configured proxy. Configuration can be accessed
+ * with linkProperties */
+ STATIC,
+ /* no proxy details are assigned, this is used to indicate
+ * that any existing proxy settings should be retained */
+ UNASSIGNED
+ }
+ /**
+ * @hide
+ */
+ public ProxySettings proxySettings;
+ /**
+ * @hide
+ */
+ public LinkProperties linkProperties;
public WifiConfiguration() {
- networkId = -1;
+ networkId = INVALID_NETWORK_ID;
SSID = null;
BSSID = null;
priority = 0;
@@ -312,6 +362,9 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : enterpriseFields) {
field.setValue(null);
}
+ ipAssignment = IpAssignment.UNASSIGNED;
+ proxySettings = ProxySettings.UNASSIGNED;
+ linkProperties = new LinkProperties();
}
public String toString() {
@@ -393,6 +446,13 @@ public class WifiConfiguration implements Parcelable {
if (value != null) sbuf.append(value);
}
sbuf.append('\n');
+ sbuf.append("IP assignment: " + ipAssignment.toString());
+ sbuf.append("\n");
+ sbuf.append("Proxy settings: " + proxySettings.toString());
+ sbuf.append("\n");
+ sbuf.append(linkProperties.toString());
+ sbuf.append("\n");
+
return sbuf.toString();
}
@@ -427,11 +487,56 @@ public class WifiConfiguration implements Parcelable {
dest.writeInt(nextSetBit);
}
+ /** @hide */
+ public int getAuthType() {
+ if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ return KeyMgmt.WPA_PSK;
+ } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
+ return KeyMgmt.WPA2_PSK;
+ } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
+ return KeyMgmt.WPA_EAP;
+ } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+ return KeyMgmt.IEEE8021X;
+ }
+ return KeyMgmt.NONE;
+ }
+
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
}
+ /** copy constructor {@hide} */
+ public WifiConfiguration(WifiConfiguration source) {
+ if (source != null) {
+ networkId = source.networkId;
+ status = source.status;
+ SSID = source.SSID;
+ BSSID = source.BSSID;
+ preSharedKey = source.preSharedKey;
+
+ wepKeys = new String[4];
+ for (int i = 0; i < wepKeys.length; i++)
+ wepKeys[i] = source.wepKeys[i];
+
+ wepTxKeyIndex = source.wepTxKeyIndex;
+ priority = source.priority;
+ hiddenSSID = source.hiddenSSID;
+ allowedKeyManagement = (BitSet) source.allowedKeyManagement.clone();
+ allowedProtocols = (BitSet) source.allowedProtocols.clone();
+ allowedAuthAlgorithms = (BitSet) source.allowedAuthAlgorithms.clone();
+ allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
+ allowedGroupCiphers = (BitSet) source.allowedGroupCiphers.clone();
+
+ for (int i = 0; i < source.enterpriseFields.length; i++) {
+ enterpriseFields[i].setValue(source.enterpriseFields[i].value());
+ }
+ ipAssignment = source.ipAssignment;
+ proxySettings = source.proxySettings;
+ linkProperties = new LinkProperties(source.linkProperties);
+ }
+ }
+
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(networkId);
@@ -454,6 +559,9 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : enterpriseFields) {
dest.writeString(field.value());
}
+ dest.writeString(ipAssignment.name());
+ dest.writeString(proxySettings.name());
+ dest.writeParcelable(linkProperties, flags);
}
/** Implement the Parcelable interface {@hide} */
@@ -480,6 +588,10 @@ public class WifiConfiguration implements Parcelable {
for (EnterpriseField field : config.enterpriseFields) {
field.setValue(in.readString());
}
+
+ config.ipAssignment = IpAssignment.valueOf(in.readString());
+ config.proxySettings = ProxySettings.valueOf(in.readString());
+ config.linkProperties = in.readParcelable(null);
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 4312bfd..f60ae48 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -19,7 +19,11 @@ package android.net.wifi;
import android.os.Parcelable;
import android.os.Parcel;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkUtils;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
import java.util.EnumMap;
/**
@@ -61,7 +65,7 @@ public class WifiInfo implements Parcelable {
public static final String LINK_SPEED_UNITS = "Mbps";
private int mLinkSpeed;
- private int mIpAddress;
+ private InetAddress mIpAddress;
private String mMacAddress;
@@ -72,7 +76,6 @@ public class WifiInfo implements Parcelable {
mSupplicantState = SupplicantState.UNINITIALIZED;
mRssi = -9999;
mLinkSpeed = -1;
- mIpAddress = 0;
mHiddenSSID = false;
}
@@ -172,12 +175,13 @@ public class WifiInfo implements Parcelable {
mSupplicantState = state;
}
- void setIpAddress(int address) {
+ void setInetAddress(InetAddress address) {
mIpAddress = address;
}
public int getIpAddress() {
- return mIpAddress;
+ if (mIpAddress == null || mIpAddress instanceof Inet6Address) return 0;
+ return NetworkUtils.inetAddressToInt(mIpAddress);
}
/**
@@ -251,7 +255,12 @@ public class WifiInfo implements Parcelable {
dest.writeInt(mNetworkId);
dest.writeInt(mRssi);
dest.writeInt(mLinkSpeed);
- dest.writeInt(mIpAddress);
+ if (mIpAddress != null) {
+ dest.writeByte((byte)1);
+ dest.writeByteArray(mIpAddress.getAddress());
+ } else {
+ dest.writeByte((byte)0);
+ }
dest.writeString(getSSID());
dest.writeString(mBSSID);
dest.writeString(mMacAddress);
@@ -266,7 +275,11 @@ public class WifiInfo implements Parcelable {
info.setNetworkId(in.readInt());
info.setRssi(in.readInt());
info.setLinkSpeed(in.readInt());
- info.setIpAddress(in.readInt());
+ if (in.readByte() == 1) {
+ try {
+ info.setInetAddress(InetAddress.getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) {}
+ }
info.setSSID(in.readString());
info.mBSSID = in.readString();
info.mMacAddress = in.readString();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f883588..2e49a77 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -18,12 +18,16 @@ package android.net.wifi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
import android.net.DhcpInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.os.Messenger;
+
+import com.android.internal.util.AsyncChannel;
import java.util.List;
@@ -60,7 +64,7 @@ public class WifiManager {
* Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
* enabling, disabling, or unknown. One extra provides this state as an int.
* Another extra provides the previous state, if available.
- *
+ *
* @see #EXTRA_WIFI_STATE
* @see #EXTRA_PREVIOUS_WIFI_STATE
*/
@@ -71,7 +75,7 @@ public class WifiManager {
* The lookup key for an int that indicates whether Wi-Fi is enabled,
* disabled, enabling, disabling, or unknown. Retrieve it with
* {@link android.content.Intent#getIntExtra(String,int)}.
- *
+ *
* @see #WIFI_STATE_DISABLED
* @see #WIFI_STATE_DISABLING
* @see #WIFI_STATE_ENABLED
@@ -81,22 +85,22 @@ public class WifiManager {
public static final String EXTRA_WIFI_STATE = "wifi_state";
/**
* The previous Wi-Fi state.
- *
+ *
* @see #EXTRA_WIFI_STATE
*/
public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
-
+
/**
* Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if
* it finishes successfully.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
public static final int WIFI_STATE_DISABLING = 0;
/**
* Wi-Fi is disabled.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -104,14 +108,14 @@ public class WifiManager {
/**
* Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if
* it finishes successfully.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
public static final int WIFI_STATE_ENABLING = 2;
/**
* Wi-Fi is enabled.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -119,7 +123,7 @@ public class WifiManager {
/**
* Wi-Fi is in an unknown state. This state will occur when an error happens while enabling
* or disabling.
- *
+ *
* @see #WIFI_STATE_CHANGED_ACTION
* @see #getWifiState()
*/
@@ -131,7 +135,6 @@ public class WifiManager {
*
* @hide
*/
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED";
@@ -166,7 +169,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_DISABLING = 0;
+ public static final int WIFI_AP_STATE_DISABLING = 10;
/**
* Wi-Fi AP is disabled.
*
@@ -175,7 +178,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_DISABLED = 1;
+ public static final int WIFI_AP_STATE_DISABLED = 11;
/**
* Wi-Fi AP is currently being enabled. The state will change to
* {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
@@ -185,7 +188,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_ENABLING = 2;
+ public static final int WIFI_AP_STATE_ENABLING = 12;
/**
* Wi-Fi AP is enabled.
*
@@ -194,7 +197,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_ENABLED = 3;
+ public static final int WIFI_AP_STATE_ENABLED = 13;
/**
* Wi-Fi AP is in a failed state. This state will occur when an error occurs during
* enabling or disabling
@@ -204,7 +207,7 @@ public class WifiManager {
*
* @hide
*/
- public static final int WIFI_AP_STATE_FAILED = 4;
+ public static final int WIFI_AP_STATE_FAILED = 14;
/**
* Broadcast intent action indicating that a connection to the supplicant has
@@ -277,6 +280,31 @@ public class WifiManager {
public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
/**
+ * Broadcast intent action for reporting errors
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ERROR_ACTION = "android.net.wifi.ERROR";
+ /**
+ * The type of error being reported
+ * @hide
+ */
+ public static final String EXTRA_ERROR_CODE = "errorCode";
+
+ /**
+ * Valid error codes
+ * @hide
+ */
+ public static final int WPS_OVERLAP_ERROR = 1;
+
+ /**
+ * Broadcast intent action indicating that the configured networks changed.
+ * This can be as a result of adding/updating/deleting a network
+ * @hide
+ */
+ public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =
+ "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+ /**
* An access point scan has completed, and results are available from the supplicant.
* Call {@link #getScanResults()} to obtain the results.
*/
@@ -294,11 +322,35 @@ public class WifiManager {
public static final String EXTRA_NEW_RSSI = "newRssi";
/**
+ * Broadcast intent action indicating that the link configuration
+ * changed on wifi.
+ * @hide
+ */
+ public static final String LINK_CONFIGURATION_CHANGED_ACTION =
+ "android.net.wifi.LINK_CONFIGURATION_CHANGED";
+
+ /**
+ * The lookup key for a {@link android.net.LinkProperties} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
+
+ /**
+ * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+
+ /**
* The network IDs of the configured networks could have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
-
+
/**
* Activity Action: Pick a Wi-Fi network to connect to.
* <p>Input: Nothing.
@@ -308,16 +360,6 @@ public class WifiManager {
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
- * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
- * {@link #WIFI_MODE_FULL} but it operates at high performance
- * at the expense of power. This mode should be used
- * only when the wifi connection needs to have minimum loss and low
- * latency as it can impact the battery life.
- * @hide
- */
- public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
-
- /**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* and will behave normally, i.e., it will attempt to automatically
* establish a connection to a remembered access point that is
@@ -335,13 +377,67 @@ public class WifiManager {
* an application in this mode.
*/
public static final int WIFI_MODE_SCAN_ONLY = 2;
+ /**
+ * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
+ * {@link #WIFI_MODE_FULL} but it operates at high performance
+ * with minimum packet loss and low packet latency even when
+ * the device screen is off. This mode will consume more power
+ * and hence should be used only when there is a need for such
+ * an active connection.
+ * <p>
+ * An example use case is when a voice connection needs to be
+ * kept active even after the device screen goes off. Holding the
+ * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
+ * connection active, but the connection can be lossy.
+ * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
+ * duration of the voice call will improve the call quality.
+ * <p>
+ * When there is no support from the hardware, this lock mode
+ * will have the same behavior as {@link #WIFI_MODE_FULL}
+ */
+ public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
/** Anything worse than or equal to this will show 0 bars. */
private static final int MIN_RSSI = -100;
-
+
/** Anything better than or equal to this will show the max bars. */
private static final int MAX_RSSI = -55;
-
+
+ /**
+ * Auto settings in the driver. The driver could choose to operate on both
+ * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band.
+ * @hide
+ */
+ public static final int WIFI_FREQUENCY_BAND_AUTO = 0;
+
+ /**
+ * Operation on 5 GHz alone
+ * @hide
+ */
+ public static final int WIFI_FREQUENCY_BAND_5GHZ = 1;
+
+ /**
+ * Operation on 2.4 GHz alone
+ * @hide
+ */
+ public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
+
+ /** List of asyncronous notifications
+ * @hide
+ */
+ public static final int DATA_ACTIVITY_NOTIFICATION = 1;
+
+ //Lowest bit indicates data reception and the second lowest
+ //bit indicates data transmitted
+ /** @hide */
+ public static final int DATA_ACTIVITY_NONE = 0x00;
+ /** @hide */
+ public static final int DATA_ACTIVITY_IN = 0x01;
+ /** @hide */
+ public static final int DATA_ACTIVITY_OUT = 0x02;
+ /** @hide */
+ public static final int DATA_ACTIVITY_INOUT = 0x03;
+
IWifiManager mService;
Handler mHandler;
@@ -354,6 +450,9 @@ public class WifiManager {
/* Number of currently active WifiLocks and MulticastLocks */
private int mActiveLockCount;
+ /* For communication with WifiService */
+ private AsyncChannel mAsyncChannel = new AsyncChannel();
+
/**
* Create a new WifiManager instance.
* Applications will almost always want to use
@@ -402,7 +501,7 @@ public class WifiManager {
* <p/>
* The new network will be marked DISABLED by default. To enable it,
* called {@link #enableNetwork}.
- *
+ *
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
* @return the ID of the newly created network description. This is used in
@@ -442,7 +541,7 @@ public class WifiManager {
/**
* Internal method for doing the RPC that creates a new network description
* or updates an existing one.
- *
+ *
* @param config The possibly sparse object containing the variables that
* are to set or updated in the network description.
* @return the ID of the network on success, {@code -1} on failure.
@@ -513,7 +612,8 @@ public class WifiManager {
*/
public boolean disconnect() {
try {
- return mService.disconnect();
+ mService.disconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -527,7 +627,8 @@ public class WifiManager {
*/
public boolean reconnect() {
try {
- return mService.reconnect();
+ mService.reconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -541,7 +642,8 @@ public class WifiManager {
*/
public boolean reassociate() {
try {
- return mService.reassociate();
+ mService.reassociate();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -626,7 +728,7 @@ public class WifiManager {
* Note: It is possible for this method to change the network IDs of
* existing networks. You should assume the network IDs can be different
* after calling this method.
- *
+ *
* @return {@code true} if the operation succeeded
*/
public boolean saveConfiguration() {
@@ -638,55 +740,62 @@ public class WifiManager {
}
/**
- * Return the number of frequency channels that are allowed
- * to be used in the current regulatory domain.
- * @return the number of allowed channels, or {@code -1} if an error occurs
+ * Set the country code.
+ * @param countryCode country code in ISO 3166 format.
+ * @param persist {@code true} if this needs to be remembered
*
- * @hide pending API council
+ * @hide
*/
- public int getNumAllowedChannels() {
+ public void setCountryCode(String country, boolean persist) {
try {
- return mService.getNumAllowedChannels();
- } catch (RemoteException e) {
- return -1;
- }
+ mService.setCountryCode(country, persist);
+ } catch (RemoteException e) { }
}
/**
- * Set the number of frequency channels that are allowed to be used
- * in the current regulatory domain. This method should be used only
- * if the correct number of channels cannot be determined automatically
- * for some reason.
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- * @param persist {@code true} if you want this remembered
- * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
- * {@code numChannels} is out of range.
- *
- * @hide pending API council
+ * Set the operational frequency band.
+ * @param band One of
+ * {@link #WIFI_FREQUENCY_BAND_AUTO},
+ * {@link #WIFI_FREQUENCY_BAND_5GHZ},
+ * {@link #WIFI_FREQUENCY_BAND_2GHZ},
+ * @param persist {@code true} if this needs to be remembered
+ * @hide
*/
- public boolean setNumAllowedChannels(int numChannels, boolean persist) {
+ public void setFrequencyBand(int band, boolean persist) {
try {
- return mService.setNumAllowedChannels(numChannels, persist);
+ mService.setFrequencyBand(band, persist);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Get the operational frequency band.
+ * @return One of
+ * {@link #WIFI_FREQUENCY_BAND_AUTO},
+ * {@link #WIFI_FREQUENCY_BAND_5GHZ},
+ * {@link #WIFI_FREQUENCY_BAND_2GHZ} or
+ * {@code -1} on failure.
+ * @hide
+ */
+ public int getFrequencyBand() {
+ try {
+ return mService.getFrequencyBand();
} catch (RemoteException e) {
- return false;
+ return -1;
}
}
/**
- * Return the list of valid values for the number of allowed radio channels
- * for various regulatory domains.
- * @return the list of channel counts, or {@code null} if the operation fails
- *
- * @hide pending API council review
+ * Check if the chipset supports dual frequency band (2.4 GHz and 5 GHz)
+ * @return {@code true} if supported, {@code false} otherwise.
+ * @hide
*/
- public int[] getValidChannelCounts() {
+ public boolean isDualBandSupported() {
try {
- return mService.getValidChannelCounts();
+ return mService.isDualBandSupported();
} catch (RemoteException e) {
- return null;
+ return false;
}
- }
+ }
/**
* Return the DHCP-assigned addresses from the last successful DHCP request,
@@ -730,20 +839,20 @@ public class WifiManager {
return WIFI_STATE_UNKNOWN;
}
}
-
+
/**
- * Return whether Wi-Fi is enabled or disabled.
+ * Return whether Wi-Fi is enabled or disabled.
* @return {@code true} if Wi-Fi is enabled
* @see #getWifiState()
*/
public boolean isWifiEnabled() {
return getWifiState() == WIFI_STATE_ENABLED;
}
-
+
/**
* Calculates the level of the signal. This should be used any time a signal
* is being shown.
- *
+ *
* @param rssi The power of the signal measured in RSSI.
* @param numLevels The number of levels to consider in the calculated
* level.
@@ -756,14 +865,15 @@ public class WifiManager {
} else if (rssi >= MAX_RSSI) {
return numLevels - 1;
} else {
- int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
- return (rssi - MIN_RSSI) / partitionSize;
+ float inputRange = (MAX_RSSI - MIN_RSSI);
+ float outputRange = (numLevels - 1);
+ return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
}
}
-
+
/**
* Compares two signal strengths.
- *
+ *
* @param rssiA The power of the first signal measured in RSSI.
* @param rssiB The power of the second signal measured in RSSI.
* @return Returns <0 if the first signal is weaker than the second signal,
@@ -851,10 +961,236 @@ public class WifiManager {
}
}
+ /**
+ * Start the driver and connect to network.
+ *
+ * This function will over-ride WifiLock and device idle status. For example,
+ * even if the device is idle or there is only a scan-only lock held,
+ * a start wifi would mean that wifi connection is kept active until
+ * a stopWifi() is sent.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean startWifi() {
+ try {
+ mService.startWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disconnect from a network (if any) and stop the driver.
+ *
+ * This function will over-ride WifiLock and device idle status. Wi-Fi
+ * stays inactive until a startWifi() is issued.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean stopWifi() {
+ try {
+ mService.stopWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a bssid to the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean addToBlacklist(String bssid) {
+ try {
+ mService.addToBlacklist(bssid);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean clearBlacklist() {
+ try {
+ mService.clearBlacklist();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /* TODO: deprecate synchronous API and open up the following API */
+
+ /* Commands to WifiService */
+ /** @hide */
+ public static final int CMD_CONNECT_NETWORK = 1;
+ /** @hide */
+ public static final int CMD_FORGET_NETWORK = 2;
+ /** @hide */
+ public static final int CMD_SAVE_NETWORK = 3;
+ /** @hide */
+ public static final int CMD_START_WPS = 4;
+
+ /* Events from WifiService */
+ /** @hide */
+ public static final int CMD_WPS_COMPLETED = 11;
+
+ /* For system use only */
+ /** @hide */
+ public static final int CMD_ENABLE_TRAFFIC_STATS_POLL = 21;
+ /** @hide */
+ public static final int CMD_TRAFFIC_STATS_POLL = 22;
+
+ /**
+ * Initiate an asynchronous channel connection setup
+ * @param srcContext is the context of the source
+ * @param srcHandler is the handler on which the source receives messages
+ * @hide
+ */
+ public void asyncConnect(Context srcContext, Handler srcHandler) {
+ mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
+ }
+
+ /**
+ * Connect to a network with the given configuration. The network also
+ * gets added to the supplicant configuration.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void connectNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+ mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);
+ }
+
+ /**
+ * Connect to a network with the given networkId.
+ *
+ * This function is used instead of a enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param networkId the network id identifiying the network in the
+ * supplicant configuration list
+ * @hide
+ */
+ public void connectNetwork(int networkId) {
+ if (networkId < 0) {
+ return;
+ }
+ mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, networkId);
+ }
+
+ /**
+ * Save the given network in the supplicant config. If the network already
+ * exists, the configuration is updated. A new network is enabled
+ * by default.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork() and saveConfiguration().
+ *
+ * For an existing network, it accomplishes the task of updateNetwork()
+ * and saveConfiguration()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void saveNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+
+ mAsyncChannel.sendMessage(CMD_SAVE_NETWORK, config);
+ }
+
+ /**
+ * Delete the network in the supplicant config.
+ *
+ * This function is used instead of a sequence of removeNetwork()
+ * and saveConfiguration().
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void forgetNetwork(int netId) {
+ if (netId < 0) {
+ return;
+ }
+
+ mAsyncChannel.sendMessage(CMD_FORGET_NETWORK, netId);
+ }
+
+ /**
+ * Start Wi-fi Protected Setup
+ *
+ * @param config WPS configuration
+ * @hide
+ */
+ public void startWps(WpsConfiguration config) {
+ if (config == null) {
+ return;
+ }
+
+ mAsyncChannel.sendMessage(CMD_START_WPS, config);
+ }
+
+ /**
+ * Get a reference to WifiService handler. This is used by a client to establish
+ * an AsyncChannel communication with WifiService
+ *
+ * @return Messenger pointing to the WifiService handler
+ * @hide
+ */
+ public Messenger getMessenger() {
+ try {
+ return mService.getMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the file in which IP and proxy configuration data is stored
+ * @hide
+ */
+ public String getConfigFile() {
+ try {
+ return mService.getConfigFile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
/**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
- * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
+ * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
* applications may hold WifiLocks, and the radio will only be allowed to turn off when no
* WifiLocks are held in any application.
*
@@ -890,7 +1226,7 @@ public class WifiManager {
* Locks the Wi-Fi radio on until {@link #release} is called.
*
* If this WifiLock is reference-counted, each call to {@code acquire} will increment the
- * reference count, and the radio will remain locked as long as the reference count is
+ * reference count, and the radio will remain locked as long as the reference count is
* above zero.
*
* If this WifiLock is not reference-counted, the first call to {@code acquire} will lock
@@ -1034,10 +1370,10 @@ public class WifiManager {
* Creates a new WifiLock.
*
* @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
- * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
- *
- * @param tag a tag for the WifiLock to identify it in debugging messages. This string is
- * never shown to the user under normal conditions, but should be descriptive
+ * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
+ * descriptions of the types of Wi-Fi locks.
+ * @param tag a tag for the WifiLock to identify it in debugging messages. This string is
+ * never shown to the user under normal conditions, but should be descriptive
* enough to identify your application and the specific WifiLock within it, if it
* holds multiple WifiLocks.
*
@@ -1048,7 +1384,7 @@ public class WifiManager {
public WifiLock createWifiLock(int lockType, String tag) {
return new WifiLock(lockType, tag);
}
-
+
/**
* Creates a new WifiLock.
*
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 266d801..ce38261 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@ package android.net.wifi;
import android.util.Log;
import android.util.Config;
import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -43,7 +42,8 @@ public class WifiMonitor {
private static final int LINK_SPEED = 5;
private static final int TERMINATING = 6;
private static final int DRIVER_STATE = 7;
- private static final int UNKNOWN = 8;
+ private static final int EAP_FAILURE = 8;
+ private static final int UNKNOWN = 9;
/** All events coming from the supplicant start with this prefix */
private static final String eventPrefix = "CTRL-EVENT-";
@@ -54,6 +54,9 @@ public class WifiMonitor {
private static final String passwordKeyMayBeIncorrectEvent =
"pre-shared key may be incorrect";
+ /* WPS events */
+ private static final String wpsOverlapEvent = "WPS-OVERLAP-DETECTED";
+
/**
* Names of events from wpa_supplicant (minus the prefix). In the
* format descriptions, * &quot;<code>x</code>&quot;
@@ -108,6 +111,17 @@ public class WifiMonitor {
* <code>state</code> is either STARTED or STOPPED
*/
private static final String driverStateEvent = "DRIVER-STATE";
+ /**
+ * <pre>
+ * CTRL-EVENT-EAP-FAILURE EAP authentication failed
+ * </pre>
+ */
+ private static final String eapFailureEvent = "EAP-FAILURE";
+
+ /**
+ * This indicates an authentication failure on EAP FAILURE event
+ */
+ private static final String eapAuthFailure = "EAP authentication failed";
/**
* Regex pattern for extracting an Ethernet-style MAC address from a string.
@@ -117,7 +131,7 @@ public class WifiMonitor {
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
/**
* This indicates the supplicant connection for the monitor is closed
@@ -139,31 +153,27 @@ public class WifiMonitor {
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateTracker tracker) {
- mWifiStateTracker = tracker;
+ public WifiMonitor(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
}
public void startMonitoring() {
new MonitorThread().start();
}
- public NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
}
-
+
public void run() {
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateTracker.notifySupplicantConnection();
+ mWifiStateMachine.notifySupplicantConnection();
} else {
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
return;
}
@@ -178,7 +188,9 @@ public class WifiMonitor {
if (!eventStr.startsWith(eventPrefix)) {
if (eventStr.startsWith(wpaEventPrefix) &&
0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) {
- handlePasswordKeyMayBeIncorrect();
+ mWifiStateMachine.notifyAuthenticationFailure();
+ } else if (eventStr.startsWith(wpsOverlapEvent)) {
+ mWifiStateMachine.notifyWpsOverlap();
}
continue;
}
@@ -207,16 +219,17 @@ public class WifiMonitor {
event = LINK_SPEED;
else if (eventName.equals(terminatingEvent))
event = TERMINATING;
- else if (eventName.equals(driverStateEvent)) {
+ else if (eventName.equals(driverStateEvent))
event = DRIVER_STATE;
- }
+ else if (eventName.equals(eapFailureEvent))
+ event = EAP_FAILURE;
else
event = UNKNOWN;
String eventData = eventStr;
if (event == DRIVER_STATE || event == LINK_SPEED)
eventData = eventData.split(" ")[1];
- else if (event == STATE_CHANGE) {
+ else if (event == STATE_CHANGE || event == EAP_FAILURE) {
int ind = eventStr.indexOf(" ");
if (ind != -1) {
eventData = eventStr.substring(ind + 1);
@@ -259,8 +272,12 @@ public class WifiMonitor {
}
// notify and exit
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
break;
+ } else if (event == EAP_FAILURE) {
+ if (eventData.startsWith(eapAuthFailure)) {
+ mWifiStateMachine.notifyAuthenticationFailure();
+ }
} else {
handleEvent(event, eventData);
}
@@ -272,11 +289,11 @@ public class WifiMonitor {
int connectTries = 0;
while (true) {
- if (mWifiStateTracker.connectToSupplicant()) {
+ if (WifiNative.connectToSupplicant()) {
return true;
}
- if (connectTries++ < 3) {
- nap(5);
+ if (connectTries++ < 5) {
+ nap(1);
} else {
break;
}
@@ -284,20 +301,16 @@ public class WifiMonitor {
return false;
}
- private void handlePasswordKeyMayBeIncorrect() {
- mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
- }
-
private void handleDriverEvent(String state) {
if (state == null) {
return;
}
if (state.equals("STOPPED")) {
- mWifiStateTracker.notifyDriverStopped();
+ mWifiStateMachine.notifyDriverStopped();
} else if (state.equals("STARTED")) {
- mWifiStateTracker.notifyDriverStarted();
+ mWifiStateMachine.notifyDriverStarted();
} else if (state.equals("HANGED")) {
- mWifiStateTracker.notifyDriverHung();
+ mWifiStateMachine.notifyDriverHung();
}
}
@@ -318,7 +331,7 @@ public class WifiMonitor {
break;
case SCAN_RESULTS:
- mWifiStateTracker.notifyScanResultsAvailable();
+ mWifiStateMachine.notifyScanResultsAvailable();
break;
case UNKNOWN:
@@ -375,7 +388,7 @@ public class WifiMonitor {
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateTracker.notifyStateChange(networkId, BSSID, newSupplicantState);
+ mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -395,7 +408,7 @@ public class WifiMonitor {
}
}
}
- mWifiStateTracker.notifyStateChange(newState, BSSID, networkId);
+ mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 25f05c0..909605dc 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -16,8 +16,6 @@
package android.net.wifi;
-import android.net.DhcpInfo;
-
/**
* Native calls for sending requests to the supplicant daemon, and for
* receiving asynchronous events. All methods of the form "xxxxCommand()"
@@ -37,17 +35,29 @@ public class WifiNative {
static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
-
+
public native static String getErrorString(int errorCode);
public native static boolean loadDriver();
-
+
+ public native static boolean isDriverLoaded();
+
public native static boolean unloadDriver();
public native static boolean startSupplicant();
-
+
+ /* Does a graceful shutdown of supplicant.
+ *
+ * Note that underneath we use a harsh-sounding "terminate" supplicant command
+ * for a graceful stop and a mild-sounding "stop" interface
+ * to kill the process
+ */
public native static boolean stopSupplicant();
+ /* Sends a kill signal to supplicant. To be used when we have lost connection
+ or when the supplicant is hung */
+ public native static boolean killSupplicant();
+
public native static boolean connectToSupplicant();
public native static void closeSupplicantConnection();
@@ -55,7 +65,7 @@ public class WifiNative {
public native static boolean pingCommand();
public native static boolean scanCommand(boolean forceActive);
-
+
public native static boolean setScanModeCommand(boolean setActive);
public native static String listNetworksCommand();
@@ -69,7 +79,7 @@ public class WifiNative {
public native static boolean removeNetworkCommand(int netId);
public native static boolean enableNetworkCommand(int netId, boolean disableOthers);
-
+
public native static boolean disableNetworkCommand(int netId);
public native static boolean reconnectCommand();
@@ -109,15 +119,15 @@ public class WifiNative {
public native static boolean setPowerModeCommand(int mode);
- public native static int getPowerModeCommand();
+ public native static int getBandCommand();
- public native static boolean setNumAllowedChannelsCommand(int numChannels);
+ public native static boolean setBandCommand(int band);
- public native static int getNumAllowedChannelsCommand();
+ public native static int getPowerModeCommand();
/**
* Sets the bluetooth coexistence mode.
- *
+ *
* @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
* {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
* {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
@@ -134,7 +144,7 @@ public class WifiNative {
* @return {@code true} if the command succeeded, {@code false} otherwise.
*/
public native static boolean setBluetoothCoexistenceScanModeCommand(boolean setCoexScanMode);
-
+
public native static boolean saveConfigCommand();
public native static boolean reloadConfigCommand();
@@ -145,15 +155,21 @@ public class WifiNative {
public native static boolean clearBlacklistCommand();
- public native static boolean doDhcpRequest(DhcpInfo results);
+ public native static boolean startWpsPbcCommand(String bssid);
+
+ public native static boolean startWpsWithPinFromAccessPointCommand(String bssid, String apPin);
- public native static String getDhcpError();
+ public native static String startWpsWithPinFromDeviceCommand(String bssid);
public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+ public native static boolean setCountryCodeCommand(String countryCode);
+
/**
* Wait for the supplicant to send an event, returning the event string.
* @return the event string sent by the supplicant.
*/
public native static String waitForEvent();
+
+ public native static void enableBackgroundScan(boolean enable);
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 0000000..4346b327
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3006 @@
+/*
+ * 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.AlarmManager;
+import android.app.PendingIntent;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.DhcpInfo;
+import android.net.DhcpInfoInternal;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkUtils;
+import android.net.wifi.WpsResult.Status;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+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
+ */
+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 LruCache<String, ScanResult> mScanResultCache;
+
+ private String mInterfaceName;
+
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private int mLastNetworkId;
+ private boolean mEnableRssiPolling = false;
+ private boolean mEnableBackgroundScan = false;
+ private int mRssiPollToken = 0;
+ private int mReconnectCount = 0;
+ private boolean mIsScanMode = false;
+ private boolean mScanResultIsPending = false;
+
+ private boolean mBluetoothConnectionActive = false;
+
+ /**
+ * Interval in milliseconds between polling for RSSI
+ * and linkspeed information
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ /**
+ * Delay between supplicant restarts upon failure to establish connection
+ */
+ private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
+
+ /**
+ * Number of times we attempt to restart supplicant
+ */
+ private static final int SUPPLICANT_RESTART_TRIES = 5;
+
+ private int mSupplicantRestartCount = 0;
+
+ private LinkProperties mLinkProperties;
+
+ // Wakelock held during wifi start/stop and driver load/unload
+ private PowerManager.WakeLock mWakeLock;
+
+ private Context mContext;
+
+ private DhcpInfoInternal mDhcpInfoInternal;
+ private WifiInfo mWifiInfo;
+ private NetworkInfo mNetworkInfo;
+ private SupplicantStateTracker mSupplicantStateTracker;
+ private WpsStateMachine mWpsStateMachine;
+
+ private AlarmManager mAlarmManager;
+ private PendingIntent mScanIntent;
+ /* Tracks current frequency mode */
+ private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
+
+ // Channel for sending replies.
+ private AsyncChannel mReplyChannel = new AsyncChannel();
+
+ // 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 */
+ static final int CMD_LOAD_DRIVER = 1;
+ /* Unload the driver */
+ static final int CMD_UNLOAD_DRIVER = 2;
+ /* Indicates driver load succeeded */
+ static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ /* Indicates driver load failed */
+ static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ /* Indicates driver unload succeeded */
+ static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ /* Indicates driver unload failed */
+ static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+
+ /* Start the supplicant */
+ static final int CMD_START_SUPPLICANT = 11;
+ /* Stop the supplicant */
+ static final int CMD_STOP_SUPPLICANT = 12;
+ /* Start the driver */
+ static final int CMD_START_DRIVER = 13;
+ /* Start the driver */
+ static final int CMD_STOP_DRIVER = 14;
+ /* Indicates DHCP succeded */
+ static final int CMD_IP_CONFIG_SUCCESS = 15;
+ /* Indicates DHCP failed */
+ static final int CMD_IP_CONFIG_FAILURE = 16;
+
+ /* Start the soft access point */
+ static final int CMD_START_AP = 21;
+ /* Stop the soft access point */
+ static final int CMD_STOP_AP = 22;
+
+ static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = 23;
+
+ /* Supplicant events */
+ /* Connection to supplicant established */
+ static final int SUP_CONNECTION_EVENT = 31;
+ /* Connection to supplicant lost */
+ static final int SUP_DISCONNECTION_EVENT = 32;
+ /* Driver start completed */
+ static final int DRIVER_START_EVENT = 33;
+ /* Driver stop completed */
+ static final int DRIVER_STOP_EVENT = 34;
+ /* Network connection completed */
+ static final int NETWORK_CONNECTION_EVENT = 36;
+ /* Network disconnection completed */
+ static final int NETWORK_DISCONNECTION_EVENT = 37;
+ /* Scan results are available */
+ static final int SCAN_RESULTS_EVENT = 38;
+ /* Supplicate state changed */
+ static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ /* Password failure and EAP authentication failure */
+ static final int AUTHENTICATION_FAILURE_EVENT = 40;
+ /* WPS overlap detected */
+ static final int WPS_OVERLAP_EVENT = 41;
+
+
+ /* Supplicant commands */
+ /* Is supplicant alive ? */
+ static final int CMD_PING_SUPPLICANT = 51;
+ /* Add/update a network configuration */
+ static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ /* Delete a network */
+ static final int CMD_REMOVE_NETWORK = 53;
+ /* Enable a network. The device will attempt a connection to the given network. */
+ static final int CMD_ENABLE_NETWORK = 54;
+ /* Enable all networks */
+ static final int CMD_ENABLE_ALL_NETWORKS = 55;
+ /* Disable a network. The device does not attempt a connection to the given network. */
+ static final int CMD_DISABLE_NETWORK = 56;
+ /* Blacklist network. De-prioritizes the given BSSID for connection. */
+ static final int CMD_BLACKLIST_NETWORK = 57;
+ /* Clear the blacklist network list */
+ static final int CMD_CLEAR_BLACKLIST = 58;
+ /* Save configuration */
+ static final int CMD_SAVE_CONFIG = 59;
+
+ /* Supplicant commands after driver start*/
+ /* Initiate a scan */
+ static final int CMD_START_SCAN = 71;
+ /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+ static final int CMD_SET_SCAN_MODE = 72;
+ /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+ static final int CMD_SET_SCAN_TYPE = 73;
+ /* Disconnect from a network */
+ static final int CMD_DISCONNECT = 74;
+ /* Reconnect to a network */
+ static final int CMD_RECONNECT = 75;
+ /* Reassociate to a network */
+ static final int CMD_REASSOCIATE = 76;
+ /* Controls power mode and suspend mode optimizations
+ *
+ * When high perf mode is enabled, power mode is set to
+ * POWER_MODE_ACTIVE and suspend mode optimizations are disabled
+ *
+ * When high perf mode is disabled, power mode is set to
+ * POWER_MODE_AUTO and suspend mode optimizations are enabled
+ *
+ * Suspend mode optimizations include:
+ * - packet filtering
+ * - turn off roaming
+ * - DTIM wake up settings
+ */
+ static final int CMD_SET_HIGH_PERF_MODE = 77;
+ /* Set the country code */
+ static final int CMD_SET_COUNTRY_CODE = 80;
+ /* Request connectivity manager wake lock before driver stop */
+ static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ /* Enables RSSI poll */
+ static final int CMD_ENABLE_RSSI_POLL = 82;
+ /* RSSI poll */
+ static final int CMD_RSSI_POLL = 83;
+ /* Set up packet filtering */
+ static final int CMD_START_PACKET_FILTERING = 84;
+ /* Clear packet filter */
+ static final int CMD_STOP_PACKET_FILTERING = 85;
+ /* 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.
+ */
+ static final int CMD_CONNECT_NETWORK = 86;
+ /* Save the specified network. This involves adding
+ * an enabled network (if new) and updating the
+ * config and issuing a save on supplicant config.
+ */
+ static final int CMD_SAVE_NETWORK = 87;
+ /* Delete the specified network. This involves
+ * removing the network and issuing a save on
+ * supplicant config.
+ */
+ static final int CMD_FORGET_NETWORK = 88;
+ /* Start Wi-Fi protected setup */
+ static final int CMD_START_WPS = 89;
+ /* Set the frequency band */
+ static final int CMD_SET_FREQUENCY_BAND = 90;
+ /* Enable background scan for configured networks */
+ static final int CMD_ENABLE_BACKGROUND_SCAN = 91;
+
+ /* Commands from/to the SupplicantStateTracker */
+ /* Reset the supplicant state tracker */
+ static final int CMD_RESET_SUPPLICANT_STATE = 111;
+
+ /* Commands/events reported by WpsStateMachine */
+ /* Indicates the completion of WPS activity */
+ static final int WPS_COMPLETED_EVENT = 121;
+ /* Reset the WPS state machine */
+ static final int CMD_RESET_WPS_STATE = 122;
+
+ 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;
+
+ private static final int SUCCESS = 1;
+ private static final int FAILURE = -1;
+
+ /**
+ * 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 POWER_MODE_ACTIVE = 1;
+ private static final int POWER_MODE_AUTO = 0;
+
+ /**
+ * See {@link Settings.Secure#WIFI_SCAN_INTERVAL_MS}. This is the default value if a
+ * Settings.Secure value is not present.
+ */
+ private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
+
+ private static final int MIN_RSSI = -200;
+ private static final int MAX_RSSI = 256;
+
+ /* 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 mSupplicantStartingState = new SupplicantStartingState();
+ /* Driver loaded and supplicant ready */
+ private HierarchicalState mSupplicantStartedState = new SupplicantStartedState();
+ /* Waiting for supplicant to stop and monitor to exit */
+ private HierarchicalState mSupplicantStoppingState = new SupplicantStoppingState();
+ /* 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();
+ /* Waiting for WPS to be completed*/
+ private HierarchicalState mWaitForWpsCompletionState = new WaitForWpsCompletionState();
+
+ /* Soft Ap is running */
+ private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+
+ /**
+ * 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 static final int SCAN_REQUEST = 0;
+ private static final String ACTION_START_SCAN =
+ "com.android.server.WifiManager.action.START_SCAN";
+
+ /**
+ * Keep track of whether WIFI is running.
+ */
+ private boolean mIsRunning = false;
+
+ /**
+ * Keep track of whether we last told the battery stats we had started.
+ */
+ private boolean mReportedRunning = false;
+
+ /**
+ * Most recently set source of starting WIFI.
+ */
+ private final WorkSource mRunningWifiUids = new WorkSource();
+
+ /**
+ * The last reported UIDs that were responsible for starting WIFI.
+ */
+ private final WorkSource mLastRunningWifiUids = new WorkSource();
+
+ private final IBatteryStats mBatteryStats;
+
+ public WifiStateMachine(Context context, String wlanInterface) {
+ super(TAG);
+
+ mContext = context;
+ mInterfaceName = wlanInterface;
+
+ 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);
+ mDhcpInfoInternal = new DhcpInfoInternal();
+ mWifiInfo = new WifiInfo();
+ mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler());
+ mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
+ mLinkProperties = new LinkProperties();
+
+ mNetworkInfo.setIsAvailable(false);
+ mLinkProperties.clear();
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ Intent scanIntent = new Intent(ACTION_START_SCAN, null);
+ mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ ArrayList<String> available = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ ArrayList<String> active = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ updateTetherState(available, active);
+
+ }
+ },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ startScan(false);
+ }
+ },
+ new IntentFilter(ACTION_START_SCAN));
+
+ mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = 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(mSupplicantStartingState, mDefaultState);
+ addState(mSupplicantStartedState, mDefaultState);
+ addState(mDriverStartingState, mSupplicantStartedState);
+ addState(mDriverStartedState, mSupplicantStartedState);
+ addState(mScanModeState, mDriverStartedState);
+ addState(mConnectModeState, mDriverStartedState);
+ addState(mConnectingState, mConnectModeState);
+ addState(mConnectedState, mConnectModeState);
+ addState(mDisconnectingState, mConnectModeState);
+ addState(mDisconnectedState, mConnectModeState);
+ addState(mWaitForWpsCompletionState, mConnectModeState);
+ addState(mDriverStoppingState, mSupplicantStartedState);
+ addState(mDriverStoppedState, mSupplicantStartedState);
+ addState(mSupplicantStoppingState, mDefaultState);
+ addState(mSoftApStartedState, mDefaultState);
+
+ setInitialState(mInitialState);
+
+ if (DBG) setDbg(true);
+
+ //start the state machine
+ start();
+ }
+
+ /*********************************************************
+ * Methods exposed for public use
+ ********************************************************/
+
+ /**
+ * TODO: doc
+ */
+ public boolean syncPingSupplicant(AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void startScan(boolean forceActive) {
+ sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0));
+ }
+
+ /**
+ * 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 syncGetWifiState() {
+ return mWifiState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String syncGetWifiStateByName() {
+ 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 syncGetWifiApState() {
+ return mWifiApState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String syncGetWifiApStateByName() {
+ 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 syncRequestConnectionInfo() {
+ return mWifiInfo;
+ }
+
+ public DhcpInfo syncGetDhcpInfo() {
+ synchronized (mDhcpInfoInternal) {
+ return mDhcpInfoInternal.makeDhcpInfo();
+ }
+ }
+
+ /**
+ * 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> syncGetScanResultsList() {
+ return mScanResults;
+ }
+
+ /**
+ * Disconnect from Access Point
+ */
+ public void disconnectCommand() {
+ sendMessage(CMD_DISCONNECT);
+ }
+
+ /**
+ * Initiate a reconnection to AP
+ */
+ public void reconnectCommand() {
+ sendMessage(CMD_RECONNECT);
+ }
+
+ /**
+ * Initiate a re-association to AP
+ */
+ public void reassociateCommand() {
+ sendMessage(CMD_REASSOCIATE);
+ }
+
+ /**
+ * Add a network synchronously
+ *
+ * @return network id of the new network
+ */
+ public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
+ int result = resultMsg.arg1;
+ resultMsg.recycle();
+ return result;
+ }
+
+ public List<WifiConfiguration> syncGetConfiguredNetworks() {
+ return WifiConfigStore.getConfiguredNetworks();
+ }
+
+ /**
+ * Delete a network
+ *
+ * @param networkId id of the network to be removed
+ */
+ public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * 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 syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
+ disableOthers ? 1 : 0);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * Disable a network
+ *
+ * @param netId network id of the network
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * 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) {
+ /* arg1 is used to indicate netId, force a netId value of -1 when
+ * we are passing a configuration since the default value of
+ * 0 is a valid netId
+ */
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+ }
+
+ public void saveNetwork(WifiConfiguration wifiConfig) {
+ sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
+ }
+
+ public void forgetNetwork(int netId) {
+ sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
+ }
+
+ public void startWps(Messenger replyTo, WpsConfiguration config) {
+ Message msg = obtainMessage(CMD_START_WPS, config);
+ msg.replyTo = replyTo;
+ sendMessage(msg);
+ }
+
+ public void enableRssiPolling(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+ }
+
+ public void enableBackgroundScan(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0));
+ }
+
+ public void enableAllNetworks() {
+ sendMessage(CMD_ENABLE_ALL_NETWORKS);
+ }
+
+ /**
+ * Start packet filtering
+ */
+ public void startPacketFiltering() {
+ sendMessage(CMD_START_PACKET_FILTERING);
+ }
+
+ /**
+ * Stop packet filtering
+ */
+ public void stopPacketFiltering() {
+ sendMessage(CMD_STOP_PACKET_FILTERING);
+ }
+
+ /**
+ * Set high performance mode of operation.
+ * Enabling would set active power mode and disable suspend optimizations;
+ * disabling would set auto power mode and enable suspend optimizations
+ * @param enable true if enable, false otherwise
+ */
+ public void setHighPerfModeEnabled(boolean enable) {
+ sendMessage(obtainMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0));
+ }
+
+ /**
+ * Set the country code
+ * @param countryCode following ISO 3166 format
+ * @param persist {@code true} if the setting should be remembered.
+ */
+ public void setCountryCode(String countryCode, boolean persist) {
+ if (persist) {
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.WIFI_COUNTRY_CODE,
+ countryCode);
+ }
+ sendMessage(obtainMessage(CMD_SET_COUNTRY_CODE, countryCode));
+ }
+
+ /**
+ * Set the operational frequency band
+ * @param band
+ * @param persist {@code true} if the setting should be remembered.
+ */
+ public void setFrequencyBand(int band, boolean persist) {
+ if (persist) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_FREQUENCY_BAND,
+ band);
+ }
+ sendMessage(obtainMessage(CMD_SET_FREQUENCY_BAND, band, 0));
+ }
+
+ /**
+ * Returns the operational frequency band
+ */
+ public int getFrequencyBand() {
+ return mFrequencyBand.get();
+ }
+
+ /**
+ * Returns the wifi configuration file
+ */
+ public String getConfigFile() {
+ return WifiConfigStore.getConfigFile();
+ }
+
+ /**
+ * Send a message indicating bluetooth adapter connection state changed
+ */
+ public void sendBluetoothAdapterStateChange(int state) {
+ sendMessage(obtainMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0));
+ }
+
+ /**
+ * Save configuration on supplicant
+ *
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * TODO: deprecate this
+ */
+ public boolean syncSaveConfig(AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * Request a wakelock with connectivity service to
+ * keep the device awake until we hand-off from wifi
+ * to an alternate network
+ */
+ public void requestCmWakeLock() {
+ sendMessage(CMD_REQUEST_CM_WAKELOCK);
+ }
+
+ public void updateBatteryWorkSource(WorkSource newSource) {
+ synchronized (mRunningWifiUids) {
+ try {
+ if (newSource != null) {
+ mRunningWifiUids.set(newSource);
+ }
+ if (mIsRunning) {
+ if (mReportedRunning) {
+ // If the work source has changed since last time, need
+ // to remove old work from battery stats.
+ if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
+ mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
+ mRunningWifiUids);
+ mLastRunningWifiUids.set(mRunningWifiUids);
+ }
+ } else {
+ // Now being started, report it.
+ mBatteryStats.noteWifiRunning(mRunningWifiUids);
+ mLastRunningWifiUids.set(mRunningWifiUids);
+ mReportedRunning = true;
+ }
+ } else {
+ if (mReportedRunning) {
+ // Last reported we were running, time to stop.
+ mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
+ mLastRunningWifiUids.clear();
+ mReportedRunning = false;
+ }
+ }
+ mWakeLock.setWorkSource(newSource);
+ } catch (RemoteException ignore) {
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String LS = System.getProperty("line.separator");
+ sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
+ sb.append("mLinkProperties ").append(mLinkProperties).append(LS);
+ sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
+ sb.append("mDhcpInfoInternal ").append(mDhcpInfoInternal).append(LS);
+ sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
+ sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
+ sb.append("mLastBssid ").append(mLastBssid).append(LS);
+ sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
+ sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
+ sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
+ sb.append("Supplicant status").append(LS)
+ .append(WifiNative.statusCommand()).append(LS).append(LS);
+
+ sb.append(WifiConfigStore.dump());
+ return sb.toString();
+ }
+
+ /*********************************************************
+ * Internal private functions
+ ********************************************************/
+
+ private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
+
+ boolean wifiTethered = false;
+ boolean wifiAvailable = false;
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ if (mCm == null) {
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ String[] wifiRegexs = mCm.getTetherableWifiRegexs();
+
+ for (String intf : available) {
+ for (String regex : wifiRegexs) {
+ if (intf.matches(regex)) {
+
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = service.getInterfaceConfig(intf);
+ if (ifcg != null) {
+ /* IP/netmask: 192.168.43.1/255.255.255.0 */
+ ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
+ "192.168.43.1"), 24);
+ ifcg.interfaceFlags = "[up]";
+
+ service.setInterfaceConfig(intf, ifcg);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + intf + ", :" + e);
+ setWifiApEnabled(null, false);
+ return;
+ }
+
+ if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Error tethering on " + intf);
+ setWifiApEnabled(null, false);
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the country code from the system setting value, if any.
+ */
+ private void setCountryCode() {
+ String countryCode = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.WIFI_COUNTRY_CODE);
+ if (countryCode != null && !countryCode.isEmpty()) {
+ setCountryCode(countryCode, false);
+ } else {
+ //use driver default
+ }
+ }
+
+ /**
+ * Set the frequency band from the system setting value, if any.
+ */
+ private void setFrequencyBand() {
+ int band = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
+ setFrequencyBand(band, false);
+ }
+
+ private void setWifiState(int wifiState) {
+ final int previousWifiState = mWifiState.get();
+
+ try {
+ if (wifiState == WIFI_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn();
+ } else if (wifiState == WIFI_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to note battery stats in wifi");
+ }
+
+ mWifiState.set(wifiState);
+
+ if (DBG) Log.d(TAG, "setWifiState: " + syncGetWifiStateByName());
+
+ 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();
+ } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to note battery stats in wifi");
+ }
+
+ // Update state
+ mWifiApState.set(wifiApState);
+
+ if (DBG) Log.d(TAG, "setWifiApState: " + syncGetWifiApStateByName());
+
+ 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 {
+ //TODO: hidden network handling
+ }
+ }
+ lineBeg = lineEnd + 1;
+ }
+ }
+
+ mScanResults = scanList;
+ }
+
+ private String fetchSSID() {
+ String status = WifiNative.statusCommand();
+ if (status == null) {
+ return null;
+ }
+ // extract ssid from a series of "name=value"
+ String[] lines = status.split("\n");
+ for (String line : lines) {
+ String[] prop = line.split(" *= *");
+ if (prop.length < 2) continue;
+ String name = prop[0];
+ String value = prop[1];
+ if (name.equalsIgnoreCase("ssid")) return value;
+ }
+ return null;
+ }
+
+ /*
+ * Fetch RSSI and linkspeed on current connection
+ */
+ private void fetchRssiAndLinkSpeedNative() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // 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(MIN_RSSI);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ private void setHighPerfModeEnabledNative(boolean enable) {
+ if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
+ Log.e(TAG, "set suspend optimizations failed!");
+ }
+ if (enable) {
+ if (!WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE)) {
+ Log.e(TAG, "set power mode active failed!");
+ }
+ } else {
+ if (!WifiNative.setPowerModeCommand(POWER_MODE_AUTO)) {
+ Log.e(TAG, "set power mode auto failed!");
+ }
+ }
+ }
+
+ private void configureLinkProperties() {
+ if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+ mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
+ } else {
+ synchronized (mDhcpInfoInternal) {
+ mLinkProperties = mDhcpInfoInternal.makeLinkProperties();
+ }
+ mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+ }
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ Log.d(TAG, "netId=" + mLastNetworkId + " Link configured: " + mLinkProperties.toString());
+ }
+
+ private int getMaxDhcpRetries() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+ DEFAULT_MAX_DHCP_RETRIES);
+ }
+
+ private void sendScanResultsAvailableBroadcast() {
+ Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ 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_LINK_PROPERTIES, mLinkProperties);
+ if (bssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendErrorBroadcast(int errorCode) {
+ Intent intent = new Intent(WifiManager.ERROR_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_ERROR_CODE, errorCode);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendLinkConfigurationChangedBroadcast() {
+ Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ 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 setNetworkDetailedState(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 DetailedState getNetworkDetailedState() {
+ return mNetworkInfo.getDetailedState();
+ }
+
+ /**
+ * 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);
+
+ /* Reset data structures */
+ mWifiInfo.setInetAddress(null);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
+ mWifiInfo.setNetworkId(-1);
+ mWifiInfo.setRssi(MIN_RSSI);
+ mWifiInfo.setLinkSpeed(-1);
+
+ /* send event to CM & network change broadcast */
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ /* Clear network properties */
+ mLinkProperties.clear();
+
+ mLastBssid= null;
+ mLastNetworkId = -1;
+
+ }
+
+
+ /*********************************************************
+ * Notifications from WifiMonitor
+ ********************************************************/
+
+ /**
+ * Stores supplicant state change information passed from WifiMonitor
+ */
+ static class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, SupplicantState state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ SupplicantState state;
+ }
+
+ /**
+ * Send the tracker a notification that a user provided
+ * configuration caused authentication failure - this could
+ * be a password failure or a EAP authentication failure
+ */
+ void notifyAuthenticationFailure() {
+ sendMessage(AUTHENTICATION_FAILURE_EVENT);
+ }
+
+ /**
+ * Send a notification that the supplicant has detected overlapped
+ * WPS sessions
+ */
+ void notifyWpsOverlap() {
+ sendMessage(WPS_OVERLAP_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 connection to the supplicant
+ * daemon is lost
+ */
+ 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, networkId, 0, BSSID));
+ } else {
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, networkId, 0, BSSID));
+ }
+ }
+
+ /**
+ * 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");
+ switch (message.what) {
+ case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
+ mBluetoothConnectionActive = (message.arg1 !=
+ BluetoothAdapter.STATE_DISCONNECTED);
+ break;
+ /* Synchronous call returns */
+ case CMD_PING_SUPPLICANT:
+ case CMD_ENABLE_NETWORK:
+ case CMD_DISABLE_NETWORK:
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ case CMD_REMOVE_NETWORK:
+ case CMD_SAVE_CONFIG:
+ mReplyChannel.replyToMessage(message, message.what, FAILURE);
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ break;
+ case CMD_ENABLE_BACKGROUND_SCAN:
+ mEnableBackgroundScan = (message.arg1 == 1);
+ 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_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ 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 AUTHENTICATION_FAILURE_EVENT:
+ case WPS_OVERLAP_EVENT:
+ case CMD_BLACKLIST_NETWORK:
+ case CMD_CLEAR_BLACKLIST:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ case CMD_REQUEST_CM_WAKELOCK:
+ case CMD_CONNECT_NETWORK:
+ case CMD_SAVE_NETWORK:
+ case CMD_FORGET_NETWORK:
+ case CMD_RSSI_POLL:
+ case CMD_ENABLE_ALL_NETWORKS:
+ break;
+ case CMD_START_WPS:
+ /* Return failure when the state machine cannot handle WPS initiation*/
+ mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
+ new WpsResult(Status.FAILURE));
+ 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() {
+ mWakeLock.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);
+ }
+ mWakeLock.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_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ 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();
+ transitionTo(mSupplicantStartingState);
+ } 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 softap start " + e);
+ try {
+ nwService.stopAccessPoint();
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch (Exception ee) {
+ Log.e(TAG, "Exception during softap restart : " + ee);
+ 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");
+ mWakeLock.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;
+ }
+ }
+ mWakeLock.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_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ 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 SupplicantStartingState 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");
+ setWifiState(WIFI_STATE_ENABLED);
+ mSupplicantRestartCount = 0;
+ /* Reset the supplicant state to indicate the supplicant
+ * state is not known at this time */
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
+ mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
+ /* Initialize data structures */
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+ WifiConfigStore.initialize(mContext);
+
+ //TODO: initialize and fix multicast filtering
+ //mWM.initializeMulticastFiltering();
+
+ sendSupplicantConnectionChangedBroadcast(true);
+ transitionTo(mDriverStartedState);
+ break;
+ case SUP_DISCONNECTION_EVENT:
+ if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
+ Log.e(TAG, "Failed to setup control channel, restart supplicant");
+ WifiNative.killSupplicant();
+ transitionTo(mDriverLoadedState);
+ sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
+ } else {
+ mSupplicantRestartCount = 0;
+ Log.e(TAG, "Failed " + mSupplicantRestartCount +
+ " times to start supplicant, unload driver");
+ transitionTo(mDriverLoadedState);
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ 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_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ 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 SupplicantStartedState 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;
+ /* Wifi is available as long as we have a connection to supplicant */
+ mNetworkInfo.setIsAvailable(true);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ WifiConfiguration config;
+ boolean eventLoggingEnabled = true;
+ switch(message.what) {
+ case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
+ Log.d(TAG, "stopping supplicant");
+ if (!WifiNative.stopSupplicant()) {
+ Log.e(TAG, "Failed to stop supplicant, issue kill");
+ WifiNative.killSupplicant();
+ }
+ mNetworkInfo.setIsAvailable(false);
+ handleNetworkDisconnect();
+ setWifiState(WIFI_STATE_DISABLING);
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
+ mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
+ transitionTo(mSupplicantStoppingState);
+ break;
+ case SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
+ Log.e(TAG, "Connection lost, restart supplicant");
+ WifiNative.killSupplicant();
+ WifiNative.closeSupplicantConnection();
+ mNetworkInfo.setIsAvailable(false);
+ handleNetworkDisconnect();
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
+ mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
+ transitionTo(mDriverLoadedState);
+ sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
+ break;
+ case SCAN_RESULTS_EVENT:
+ eventLoggingEnabled = false;
+ setScanResults(WifiNative.scanResultsCommand());
+ sendScanResultsAvailableBroadcast();
+ mScanResultIsPending = false;
+ break;
+ case CMD_PING_SUPPLICANT:
+ boolean ok = WifiNative.pingCommand();
+ mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ config = (WifiConfiguration) message.obj;
+ mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+ WifiConfigStore.addOrUpdateNetwork(config));
+ break;
+ case CMD_REMOVE_NETWORK:
+ ok = WifiConfigStore.removeNetwork(message.arg1);
+ mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
+ case CMD_ENABLE_NETWORK:
+ ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+ mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
+ case CMD_ENABLE_ALL_NETWORKS:
+ WifiConfigStore.enableAllNetworks();
+ break;
+ case CMD_DISABLE_NETWORK:
+ ok = WifiConfigStore.disableNetwork(message.arg1);
+ mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
+ case CMD_BLACKLIST_NETWORK:
+ WifiNative.addToBlacklistCommand((String)message.obj);
+ break;
+ case CMD_CLEAR_BLACKLIST:
+ WifiNative.clearBlacklistCommand();
+ break;
+ case CMD_SAVE_CONFIG:
+ ok = WifiConfigStore.saveConfig();
+ mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+
+ // 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;
+ /* Cannot start soft AP while in client mode */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ 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;
+ WifiConfigStore.saveNetwork(config);
+ break;
+ case CMD_FORGET_NETWORK:
+ WifiConfigStore.forgetNetwork(message.arg1);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ if (eventLoggingEnabled) {
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(false);
+ }
+ }
+
+ class SupplicantStoppingState 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.e(TAG, "Supplicant connection received while stopping");
+ break;
+ case SUP_DISCONNECTION_EVENT:
+ Log.d(TAG, "Supplicant connection lost");
+ WifiNative.closeSupplicantConnection();
+ transitionTo(mDriverLoadedState);
+ 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_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ 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 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 AUTHENTICATION_FAILURE_EVENT:
+ case WPS_OVERLAP_EVENT:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ 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());
+
+ mIsRunning = true;
+ updateBatteryWorkSource(null);
+
+ /**
+ * Enable bluetooth coexistence scan mode when bluetooth connection is active.
+ * When this mode is on, some of the low-level scan parameters used by the
+ * driver are changed to reduce interference with bluetooth
+ */
+ WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
+ /* set country code */
+ setCountryCode();
+ /* set frequency band of operation */
+ setFrequencyBand();
+ /* initialize network state */
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+
+ if (mIsScanMode) {
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.disconnectCommand();
+ transitionTo(mScanModeState);
+ } else {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ WifiNative.reconnectCommand();
+ transitionTo(mDisconnectedState);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ boolean eventLoggingEnabled = true;
+ switch(message.what) {
+ case CMD_SET_SCAN_TYPE:
+ if (message.arg1 == SCAN_ACTIVE) {
+ WifiNative.setScanModeCommand(true);
+ } else {
+ WifiNative.setScanModeCommand(false);
+ }
+ break;
+ case CMD_START_SCAN:
+ eventLoggingEnabled = false;
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ mScanResultIsPending = true;
+ break;
+ case CMD_SET_HIGH_PERF_MODE:
+ setHighPerfModeEnabledNative(message.arg1 == 1);
+ break;
+ case CMD_SET_COUNTRY_CODE:
+ String country = (String) message.obj;
+ Log.d(TAG, "set country code " + country);
+ if (!WifiNative.setCountryCodeCommand(country.toUpperCase())) {
+ Log.e(TAG, "Failed to set country code " + country);
+ }
+ break;
+ case CMD_SET_FREQUENCY_BAND:
+ int band = message.arg1;
+ Log.d(TAG, "set frequency band " + band);
+ if (WifiNative.setBandCommand(band)) {
+ mFrequencyBand.set(band);
+ //Fetch the latest scan results when frequency band is set
+ startScan(true);
+ } else {
+ Log.e(TAG, "Failed to set frequency band " + band);
+ }
+ break;
+ case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
+ mBluetoothConnectionActive = (message.arg1 !=
+ BluetoothAdapter.STATE_DISCONNECTED);
+ WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
+ break;
+ case CMD_STOP_DRIVER:
+ mWakeLock.acquire();
+ WifiNative.stopDriverCommand();
+ transitionTo(mDriverStoppingState);
+ mWakeLock.release();
+ break;
+ case CMD_START_PACKET_FILTERING:
+ WifiNative.startPacketFiltering();
+ break;
+ case CMD_STOP_PACKET_FILTERING:
+ WifiNative.stopPacketFiltering();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ if (eventLoggingEnabled) {
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mIsRunning = false;
+ updateBatteryWorkSource(null);
+ mScanResults = null;
+ }
+ }
+
+ 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_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ 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");
+ switch (message.what) {
+ case CMD_START_DRIVER:
+ mWakeLock.acquire();
+ WifiNative.startDriverCommand();
+ transitionTo(mDriverStartingState);
+ mWakeLock.release();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return 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");
+ 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;
+ /* 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");
+ StateChangeResult stateChangeResult;
+ switch(message.what) {
+ case AUTHENTICATION_FAILURE_EVENT:
+ mSupplicantStateTracker.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+ break;
+ case WPS_OVERLAP_EVENT:
+ /* We just need to broadcast the error */
+ sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = 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, state.ordinal());
+ mWifiInfo.setSupplicantState(state);
+ // Network id is only valid when we start connecting
+ if (SupplicantState.isConnecting(state)) {
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ }
+ if (state == SupplicantState.ASSOCIATING) {
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ }
+
+ mSupplicantStateTracker.sendMessage(Message.obtain(message));
+ mWpsStateMachine.sendMessage(Message.obtain(message));
+ break;
+ /* Do a redundant disconnect without transition */
+ case CMD_DISCONNECT:
+ WifiNative.disconnectCommand();
+ break;
+ case CMD_RECONNECT:
+ WifiNative.reconnectCommand();
+ break;
+ case CMD_REASSOCIATE:
+ WifiNative.reassociateCommand();
+ break;
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+
+ /* We connect to a specific network by issuing a select
+ * to the WifiConfigStore. This enables the network,
+ * while disabling all other networks in the supplicant.
+ * Disabling a connected network will cause a disconnection
+ * from the network. A reconnectCommand() will then initiate
+ * a connection to the enabled network.
+ */
+ if (config != null) {
+ WifiConfigStore.selectNetwork(config);
+ } else {
+ WifiConfigStore.selectNetwork(netId);
+ }
+
+ /* The state tracker handles enabling networks upon completion/failure */
+ mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);
+
+ WifiNative.reconnectCommand();
+
+ /* Expect a disconnection from the old connection */
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_START_WPS:
+ mWpsStateMachine.sendMessage(Message.obtain(message));
+ transitionTo(mWaitForWpsCompletionState);
+ 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");
+ mLastNetworkId = message.arg1;
+ mLastBssid = (String) message.obj;
+
+ //TODO: make supplicant modification to push this in events
+ mWifiInfo.setSSID(fetchSSID());
+ mWifiInfo.setBSSID(mLastBssid);
+ mWifiInfo.setNetworkId(mLastNetworkId);
+ /* send event to CM & network change broadcast */
+ setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mConnectingState);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ handleNetworkDisconnect();
+ transitionTo(mDisconnectedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectingState extends HierarchicalState {
+ boolean mModifiedBluetoothCoexistenceMode;
+ int mPowerMode;
+ boolean mUseStaticIp;
+ Thread mDhcpThread;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
+ if (!mUseStaticIp) {
+ mDhcpThread = null;
+ mModifiedBluetoothCoexistenceMode = false;
+ mPowerMode = POWER_MODE_AUTO;
+
+ if (!mBluetoothConnectionActive) {
+ /*
+ * 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.
+ */
+ mModifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+
+ mPowerMode = WifiNative.getPowerModeCommand();
+ if (mPowerMode < 0) {
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
+ mPowerMode = POWER_MODE_AUTO;
+ }
+ if (mPowerMode != POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE);
+ }
+
+ Log.d(TAG, "DHCP request started");
+ mDhcpThread = new Thread(new Runnable() {
+ public void run() {
+ DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+ if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal)) {
+ Log.d(TAG, "DHCP request succeeded");
+ synchronized (mDhcpInfoInternal) {
+ mDhcpInfoInternal = dhcpInfoInternal;
+ }
+ WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.d(TAG, "DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ });
+ mDhcpThread.start();
+ } else {
+ DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration(
+ mLastNetworkId);
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService netd = INetworkManagementService.Stub.asInterface(b);
+ InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.addr = dhcpInfoInternal.makeLinkAddress();
+ ifcg.interfaceFlags = "[up]";
+ try {
+ netd.setInterfaceConfig(mInterfaceName, ifcg);
+ Log.v(TAG, "Static IP configuration succeeded");
+ synchronized (mDhcpInfoInternal) {
+ mDhcpInfoInternal = dhcpInfoInternal;
+ }
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } catch (RemoteException re) {
+ Log.v(TAG, "Static IP configuration failed: " + re);
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ } catch (IllegalStateException e) {
+ Log.v(TAG, "Static IP configuration failed: " + e);
+ 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:
+ mLastSignalLevel = -1; // force update of signal strength
+ InetAddress addr;
+ synchronized (mDhcpInfoInternal) {
+ addr = NetworkUtils.numericToInetAddress(mDhcpInfoInternal.ipAddress);
+ }
+ mWifiInfo.setInetAddress(addr);
+ configureLinkProperties();
+ if (getNetworkDetailedState() == DetailedState.CONNECTED) {
+ sendLinkConfigurationChangedBroadcast();
+ } else {
+ setNetworkDetailedState(DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ }
+ //TODO: The framework is not detecting a DHCP renewal and a possible
+ //IP change. we should detect this and send out a config change broadcast
+ transitionTo(mConnectedState);
+ break;
+ case CMD_IP_CONFIG_FAILURE:
+ mWifiInfo.setInetAddress(null);
+
+ 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);
+ WifiConfigStore.disableNetwork(mLastNetworkId);
+ mReconnectCount = 0;
+ }
+
+ /* 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:
+ WifiNative.disconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ /* Ignore connection to same network */
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ if (mWifiInfo.getNetworkId() == netId) {
+ break;
+ }
+ return NOT_HANDLED;
+ case CMD_SAVE_NETWORK:
+ deferMessage(message);
+ 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;
+ /* Defer scan when IP is being fetched */
+ case CMD_START_SCAN:
+ deferMessage(message);
+ break;
+ /* Defer any power mode changes since we must keep active power mode at DHCP */
+ case CMD_SET_HIGH_PERF_MODE:
+ 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 (mPowerMode != POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(mPowerMode);
+ }
+
+ if (mModifiedBluetoothCoexistenceMode) {
+ // 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());
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ boolean eventLoggingEnabled = true;
+ switch (message.what) {
+ case CMD_DISCONNECT:
+ WifiNative.disconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_REQUEST_CM_WAKELOCK:
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ mCm.requestNetworkTransitionWakelock(TAG);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ case CMD_START_SCAN:
+ eventLoggingEnabled = false;
+ /* When the network is connected, re-scanning can trigger
+ * a reconnection. Put it in scan-only mode during scan.
+ * When scan results are received, the mode is switched
+ * back to CONNECT_MODE.
+ */
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ /* Have the parent state handle the rest */
+ return NOT_HANDLED;
+ /* Ignore connection to same network */
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ if (mWifiInfo.getNetworkId() == netId) {
+ break;
+ }
+ return NOT_HANDLED;
+ case CMD_SAVE_NETWORK:
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+ NetworkUpdateResult result = WifiConfigStore.saveNetwork(config);
+ if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
+ if (result.hasIpChanged()) {
+ Log.d(TAG,"Reconfiguring IP on connection");
+ NetworkUtils.resetConnections(mInterfaceName);
+ transitionTo(mConnectingState);
+ }
+ if (result.hasProxyChanged()) {
+ Log.d(TAG,"Reconfiguring proxy on connection");
+ configureLinkProperties();
+ sendLinkConfigurationChangedBroadcast();
+ }
+ }
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ case CMD_RSSI_POLL:
+ eventLoggingEnabled = false;
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ fetchRssiAndLinkSpeedNative();
+ sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ fetchRssiAndLinkSpeedNative();
+ sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ if (eventLoggingEnabled) {
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ /* If a scan result is pending in connected state, the supplicant
+ * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
+ */
+ if (mScanResultIsPending) {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ }
+ }
+ }
+
+ 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;
+ /* Handle in DisconnectedState */
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ private boolean mAlarmEnabled = false;
+ private long mScanIntervalMs;
+
+ private void setScanAlarm(boolean enabled) {
+ if (enabled == mAlarmEnabled) return;
+ if (enabled) {
+ mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + mScanIntervalMs,
+ mScanIntervalMs,
+ mScanIntent);
+
+ mAlarmEnabled = true;
+ } else {
+ mAlarmManager.cancel(mScanIntent);
+ mAlarmEnabled = false;
+ }
+ }
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ mScanIntervalMs = Settings.Secure.getLong(mContext.getContentResolver(),
+ Settings.Secure.WIFI_SCAN_INTERVAL_MS, DEFAULT_SCAN_INTERVAL_MS);
+ /*
+ * We initiate background scanning if it is enabled, otherwise we
+ * initiate an infrequent scan that wakes up the device to ensure
+ * a user connects to an access point on the move
+ */
+ if (mEnableBackgroundScan) {
+ /* If a regular scan result is pending, do not initiate background
+ * scan until the scan results are returned. This is needed because
+ * initiating a background scan will cancel the regular scan and
+ * scan results will not be returned until background scanning is
+ * cleared
+ */
+ if (!mScanResultIsPending) {
+ WifiNative.enableBackgroundScan(true);
+ }
+ } else {
+ setScanAlarm(true);
+ }
+ }
+ @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;
+ case CMD_ENABLE_BACKGROUND_SCAN:
+ mEnableBackgroundScan = (message.arg1 == 1);
+ if (mEnableBackgroundScan) {
+ WifiNative.enableBackgroundScan(true);
+ setScanAlarm(false);
+ } else {
+ WifiNative.enableBackgroundScan(false);
+ setScanAlarm(true);
+ }
+ break;
+ /* Ignore network disconnect */
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
+ /* DriverStartedState does the rest of the handling */
+ return NOT_HANDLED;
+ case CMD_START_SCAN:
+ /* Disable background scan temporarily during a regular scan */
+ if (mEnableBackgroundScan) {
+ WifiNative.enableBackgroundScan(false);
+ }
+ /* Handled in parent state */
+ return NOT_HANDLED;
+ case SCAN_RESULTS_EVENT:
+ /* Re-enable background scan when a pending scan result is received */
+ if (mEnableBackgroundScan && mScanResultIsPending) {
+ WifiNative.enableBackgroundScan(true);
+ }
+ /* Handled in parent state */
+ return NOT_HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ /* No need for a background scan upon exit from a disconnected state */
+ if (mEnableBackgroundScan) {
+ WifiNative.enableBackgroundScan(false);
+ }
+ setScanAlarm(false);
+ }
+ }
+
+ class WaitForWpsCompletionState 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) {
+ /* Defer all commands that can cause connections to a different network
+ * or put the state machine out of connect mode
+ */
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_CONNECT_NETWORK:
+ case CMD_ENABLE_NETWORK:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
+ deferMessage(message);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ handleNetworkDisconnect();
+ break;
+ case WPS_COMPLETED_EVENT:
+ /* we are still disconnected until we see a network connection
+ * notification */
+ transitionTo(mDisconnectedState);
+ 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);
+
+ if (mCm == null) {
+ mCm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ if (mCm.untether(SOFTAP_IFACE) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Untether initiate failed!");
+ }
+ 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 softap set " + e);
+ try {
+ nwService.stopAccessPoint();
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch (Exception ee) {
+ Log.e(TAG, "Could not restart softap after set failed " + 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;
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index bf2d033..338cb4d 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -16,466 +16,110 @@
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;
-import android.app.ActivityManagerNative;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.LinkCapabilities;
import android.net.NetworkInfo;
+import android.net.LinkProperties;
import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
-import android.os.Message;
-import android.os.Parcelable;
import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.SystemProperties;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Config;
-import android.app.Notification;
-import android.app.PendingIntent;
-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 android.os.Message;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
*
* @hide
*/
-public class WifiStateTracker extends NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
- private static final boolean LOCAL_LOGD = Config.LOGD || false;
-
+ private static final String NETWORKTYPE = "WIFI";
private static final String TAG = "WifiStateTracker";
- // Event log tags (must be in sync with event-log-tags)
- private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
- private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
- private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
- private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
- private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
-
- // Event codes
- private static final int EVENT_SUPPLICANT_CONNECTION = 1;
- private static final int EVENT_SUPPLICANT_DISCONNECT = 2;
- private static final int EVENT_SUPPLICANT_STATE_CHANGED = 3;
- private static final int EVENT_NETWORK_STATE_CHANGED = 4;
- private static final int EVENT_SCAN_RESULTS_AVAILABLE = 5;
- private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
- private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7;
- private static final int EVENT_POLL_INTERVAL = 8;
- private static final int EVENT_DHCP_START = 9;
- private static final int EVENT_DEFERRED_DISCONNECT = 10;
- private static final int EVENT_DEFERRED_RECONNECT = 11;
- /**
- * The driver is started or stopped. The object will be the state: true for
- * started, false for stopped.
- */
- private static final int EVENT_DRIVER_STATE_CHANGED = 12;
- private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13;
- private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14;
-
- /**
- * The driver state indication.
- */
- private static final int DRIVER_STARTED = 0;
- private static final int DRIVER_STOPPED = 1;
- private static final int DRIVER_HUNG = 2;
-
- /**
- * 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_STATUS_INTERVAL_MSECS = 3000;
-
- /**
- * 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;
-
- /**
- * When a DISCONNECT event is received, we defer handling it to
- * allow for the possibility that the DISCONNECT is about to
- * be followed shortly by a CONNECT to the same network we were
- * just connected to. In such a case, we don't want to report
- * the network as down, nor do we want to reconfigure the network
- * interface, etc. If we get a CONNECT event for another network
- * within the delay window, we immediately handle the pending
- * disconnect before processing the CONNECT.<p/>
- * The five second delay is chosen somewhat arbitrarily, but is
- * meant to cover most of the cases where a DISCONNECT/CONNECT
- * happens to a network.
- */
- private static final int DISCONNECT_DELAY_MSECS = 5000;
- /**
- * When the supplicant goes idle after we do an explicit disconnect
- * following a DHCP failure, we need to kick the supplicant into
- * trying to associate with access points.
- */
- private static final int RECONNECT_DELAY_MSECS = 2000;
-
- /**
- * When the supplicant disconnects from an AP it sometimes forgets
- * to restart scanning. Wait this delay before asking it to start
- * scanning (in case it forgot). 15 sec is the standard delay between
- * scans.
- */
- private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
-
- /**
- * 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_AUTO = 0;
- private static final int DRIVER_POWER_MODE_ACTIVE = 1;
-
- /**
- * The current WPA supplicant loop state (used to detect looping behavior):
- */
- private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
-
- /**
- * The current number of WPA supplicant loop iterations:
- */
- private int mNumSupplicantLoopIterations = 0;
-
- /**
- * The current number of supplicant state changes. This is used to determine
- * if we've received any new info since we found out it was DISCONNECTED or
- * INACTIVE. If we haven't for X ms, we then request a scan - it should have
- * done that automatically, but sometimes some firmware does not.
- */
- private int mNumSupplicantStateChanges = 0;
-
- /**
- * True if we received an event that that a password-key may be incorrect.
- * If the next incoming supplicant state change event is DISCONNECT,
- * broadcast a message that we have a possible password error and disable
- * the network.
- */
- private boolean mPasswordKeyMayBeIncorrect = false;
-
- public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
- public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
-
- private WifiMonitor mWifiMonitor;
- private WifiInfo mWifiInfo;
- private List<ScanResult> mScanResults;
- private WifiManager mWM;
- private boolean mHaveIpAddress;
- private boolean mObtainingIpAddress;
- private boolean mTornDownByConnMgr;
- /**
- * A DISCONNECT event has been received, but processing it
- * is being deferred.
- */
- private boolean mDisconnectPending;
- /**
- * An operation has been performed as a result of which we expect the next event
- * will be a DISCONNECT.
- */
- private boolean mDisconnectExpected;
- private DhcpHandler mDhcpTarget;
- private DhcpInfo mDhcpInfo;
- private int mLastSignalLevel = -1;
- private String mLastBssid;
- private String mLastSsid;
- private int mLastNetworkId = -1;
- private boolean mUseStaticIp = false;
- private int mReconnectCount;
-
- /* Tracks if any network in the configuration is disabled */
- private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
-
- // used to store the (non-persisted) num determined during device boot
- // (from mcc or other phone info) before the driver is started.
- private int mNumAllowedChannels = 0;
-
- // Variables relating to the 'available networks' notification
-
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
- /**
- * Observes the static IP address settings.
- */
- private SettingsObserver mSettingsObserver;
-
- private boolean mIsScanModeActive;
- private boolean mEnableRssiPolling;
- private boolean mIsHighPerfEnabled;
- private int mPowerModeRefCount = 0;
- private int mOptimizationsDisabledRefCount = 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}
- *
- * getWifiState() is not synchronized to make sure it's always fast,
- * even when the instance lock is held on other slow operations.
- * Use a atomic variable for state.
- */
- private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
-
- // Wi-Fi run states:
- private static final int RUN_STATE_STARTING = 1;
- private static final int RUN_STATE_RUNNING = 2;
- private static final int RUN_STATE_STOPPING = 3;
- private static final int RUN_STATE_STOPPED = 4;
+ private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
+ private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
+ private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
- private static final String mRunStateNames[] = {
- "Starting",
- "Running",
- "Stopping",
- "Stopped"
- };
- private int mRunState;
+ private LinkProperties mLinkProperties;
+ private LinkCapabilities mLinkCapabilities;
+ private NetworkInfo mNetworkInfo;
+ private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN;
- private final IBatteryStats mBatteryStats;
+ /* For sending events to connectivity service handler */
+ private Handler mCsHandler;
+ private Context mContext;
+ private BroadcastReceiver mWifiStateReceiver;
+ private WifiManager mWifiManager;
- private boolean mIsScanOnly;
+ public WifiStateTracker() {
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mLinkProperties = new LinkProperties();
+ mLinkCapabilities = new LinkCapabilities();
- private BluetoothA2dp mBluetoothA2dp;
-
- private String mInterfaceName;
- private static String LS = System.getProperty("line.separator");
-
- private static String[] sDnsPropNames;
- private Runnable mReleaseWakeLockCallback;
-
- /**
- * Keep track of whether we last told the battery stats we had started.
- */
- private boolean mReportedRunning = false;
-
- /**
- * Most recently set source of starting WIFI.
- */
- private final WorkSource mRunningWifiUids = new WorkSource();
-
- /**
- * The last reported UIDs that were responsible for starting WIFI.
- */
- private final WorkSource mLastRunningWifiUids = new WorkSource();
-
- /**
- * 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 SupplicantStateChangeResult {
- SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- SupplicantState state;
- }
-
- /**
- * A structure for supplying information about a connection in
- * the CONNECTED event message that comes from the WifiMonitor
- * thread.
- */
- private static class NetworkStateChangeResult {
- NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- DetailedState state;
- String BSSID;
- int networkId;
+ mNetworkInfo.setIsAvailable(false);
+ setTeardownRequested(false);
}
- public WifiStateTracker(Context context, Handler target) {
- super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-
- mWifiInfo = new WifiInfo();
- mWifiMonitor = new WifiMonitor(this);
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- setTornDownByConnMgr(false);
- mDisconnectPending = false;
- mScanResults = new ArrayList<ScanResult>();
- // Allocate DHCP info object once, and fill it in on each request
- mDhcpInfo = new DhcpInfo();
- mRunState = RUN_STATE_STARTING;
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
- mNotificationEnabledSettingObserver.register();
-
- mSettingsObserver = new SettingsObserver(new Handler());
-
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- sDnsPropNames = new String[] {
- "dhcp." + mInterfaceName + ".dns1",
- "dhcp." + mInterfaceName + ".dns2"
- };
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ public void setTeardownRequested(boolean isRequested) {
+ mTeardownRequested.set(isRequested);
}
- /**
- * Helper method: sets the supplicant state and keeps the network
- * info updated.
- * @param state the new state
- */
- private void setSupplicantState(SupplicantState state) {
- mWifiInfo.setSupplicantState(state);
- updateNetworkInfo();
- checkPollTimer();
- }
-
- public SupplicantState getSupplicantState() {
- return mWifiInfo.getSupplicantState();
+ public boolean isTeardownRequested() {
+ return mTeardownRequested.get();
}
/**
- * Helper method: sets the supplicant state and keeps the network
- * info updated (string version).
- * @param stateName the string name of the new state
+ * Begin monitoring wifi connectivity
*/
- private void setSupplicantState(String stateName) {
- mWifiInfo.setSupplicantState(stateName);
- updateNetworkInfo();
- checkPollTimer();
- }
+ public void startMonitoring(Context context, Handler target) {
+ mCsHandler = target;
+ mContext = context;
- /**
- * Helper method: sets the boolean indicating that the connection
- * manager asked the network to be torn down (and so only the connection
- * manager can set it up again).
- * network info updated.
- * @param flag {@code true} if explicitly disabled.
- */
- private void setTornDownByConnMgr(boolean flag) {
- mTornDownByConnMgr = flag;
- updateNetworkInfo();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+
+ mWifiStateReceiver = new WifiStateReceiver();
+ mContext.registerReceiver(mWifiStateReceiver, filter);
}
/**
- * Return the IP addresses of the DNS servers available for the WLAN
- * network interface.
- * @return a list of DNS addresses, with no holes.
+ * Disable connectivity to a network
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String[] getNameServers() {
- return getNameServerList(sDnsPropNames);
+ public boolean teardown() {
+ mTeardownRequested.set(true);
+ mWifiManager.stopWifi();
+ return true;
}
/**
- * Return the name of our WLAN network interface.
- * @return the name of our interface.
+ * Re-enable connectivity to a network after a {@link #teardown()}.
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String getInterfaceName() {
- return mInterfaceName;
+ public boolean reconnect() {
+ mTeardownRequested.set(false);
+ mWifiManager.startWifi();
+ return true;
}
/**
- * Return the system properties name associated with the tcp buffer sizes
- * for this network.
+ * Turn the wireless radio off for a network.
+ * @param turnOn {@code true} to turn the radio on, {@code false}
+ * TODO: do away with return value after making MobileDataStateTracker async
*/
- public String getTcpBufferSizesPropName() {
- return "net.tcp.buffersize.wifi";
- }
-
- public void startMonitoring() {
- /*
- * Get a handle on the WifiManager. This cannot be done in our
- * constructor, because the Wifi service is not yet registered.
- */
- mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
- }
-
- public void startEventLoop() {
- mWifiMonitor.startMonitoring();
+ public boolean setRadio(boolean turnOn) {
+ mWifiManager.setWifiEnabled(turnOn);
+ return true;
}
/**
@@ -487,2190 +131,113 @@ public class WifiStateTracker extends NetworkStateTracker {
* unavailable.
* @return {@code true} if Wi-Fi connections are possible
*/
- public synchronized boolean isAvailable() {
- /*
- * TODO: Need to also look at scan results to see whether we're
- * in range of any access points. If we have scan results that
- * are no more than N seconds old, use those, otherwise, initiate
- * a scan and wait for the results. This only matters if we
- * allow mobile to be the preferred network.
- */
- SupplicantState suppState = mWifiInfo.getSupplicantState();
- return suppState != SupplicantState.UNINITIALIZED &&
- suppState != SupplicantState.INACTIVE &&
- (mTornDownByConnMgr || !isDriverStopped());
- }
-
- /**
- * {@inheritDoc}
- * There are currently no defined Wi-Fi subtypes.
- */
- public int getNetworkSubtype() {
- return 0;
- }
-
- /**
- * Helper method: updates the network info object to keep it in sync with
- * the Wi-Fi state tracker.
- */
- private void updateNetworkInfo() {
- mNetworkInfo.setIsAvailable(isAvailable());
- }
-
- /**
- * Report whether the Wi-Fi connection is fully configured for data.
- * @return {@code true} if the {@link SupplicantState} is
- * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
- */
- public boolean isConnectionCompleted() {
- return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
- }
-
- /**
- * Report whether the Wi-Fi connection has successfully acquired an IP address.
- * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
- */
- public boolean hasIpAddress() {
- return mHaveIpAddress;
- }
-
- /**
- * Send the tracker a notification that a user-entered password key
- * may be incorrect (i.e., caused authentication to fail).
- */
- void notifyPasswordKeyMayBeIncorrect() {
- sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
- }
-
- /**
- * 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 notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
- Message msg = Message.obtain(
- this, EVENT_SUPPLICANT_STATE_CHANGED,
- new SupplicantStateChangeResult(networkId, BSSID, newState));
- msg.sendToTarget();
- }
-
- /**
- * 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 notifyStateChange(DetailedState newState, String BSSID, int networkId) {
- Message msg = Message.obtain(
- this, EVENT_NETWORK_STATE_CHANGED,
- new NetworkStateChangeResult(newState, BSSID, networkId));
- msg.sendToTarget();
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- // reset the supplicant's handling of scan results to "normal" mode
- setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
- sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
- }
-
- /**
- * Send the tracker a notification that we can no longer communicate with
- * the supplicant daemon.
- */
- void notifySupplicantLost() {
- sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has been stopped.
- */
- void notifyDriverStopped() {
- // Send a driver stopped message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has been restarted after
- * having been stopped.
- */
- void notifyDriverStarted() {
- // Send a driver started message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
- }
-
- /**
- * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
- */
- void notifyDriverHung() {
- // Send a driver hanged message to our handler
- Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
- }
-
- /**
- * Set the interval timer for polling connection information
- * that is not delivered asynchronously.
- */
- private synchronized void checkPollTimer() {
- if (mEnableRssiPolling &&
- mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
- !hasMessages(EVENT_POLL_INTERVAL)) {
- sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
- }
- }
-
- /**
- * TODO: mRunState is not synchronized in some places
- * address this as part of re-architect.
- *
- * TODO: We are exposing an additional public synchronized call
- * for a wakelock optimization in WifiService. Remove it
- * when we handle the wakelock in ConnectivityService.
- */
- public synchronized boolean isDriverStopped() {
- return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
- }
-
- public void updateBatteryWorkSourceLocked(WorkSource newSource) {
- try {
- if (newSource != null) {
- mRunningWifiUids.set(newSource);
- }
- if (mRunState == RUN_STATE_RUNNING) {
- if (mReportedRunning) {
- // If the work source has changed since last time, need
- // to remove old work from battery stats.
- if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
- mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
- mRunningWifiUids);
- mLastRunningWifiUids.set(mRunningWifiUids);
- }
- } else {
- // Now being started, report it.
- mBatteryStats.noteWifiRunning(mRunningWifiUids);
- mLastRunningWifiUids.set(mRunningWifiUids);
- mReportedRunning = true;
- }
- } else if (mRunState == RUN_STATE_STOPPED) {
- if (mReportedRunning) {
- // Last reported we were running, time to stop.
- mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
- mLastRunningWifiUids.clear();
- mReportedRunning = false;
- }
- } else {
- // State in transition -- nothing to update yet.
- }
- } catch (RemoteException ignore) {
- }
- }
-
- /**
- * Set the run state to either "normal" or "scan-only".
- * @param scanOnlyMode true if the new mode should be scan-only.
- */
- public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
- // do nothing unless scan-only mode is changing
- if (mIsScanOnly != scanOnlyMode) {
- int scanType = (scanOnlyMode ?
- SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
- if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
- if (setScanResultHandling(scanType)) {
- mIsScanOnly = scanOnlyMode;
- if (!isDriverStopped()) {
- if (scanOnlyMode) {
- disconnect();
- } else {
- reconnectCommand();
- }
- }
- }
- }
- }
-
- /**
- * Set suspend mode optimizations. These include:
- * - packet filtering
- * - turn off roaming
- * - DTIM settings
- *
- * Uses reference counting to keep the suspend optimizations disabled
- * as long as one entity wants optimizations disabled.
- *
- * For example, WifiLock can keep suspend optimizations disabled
- * or the user setting (wifi never sleeps) can keep suspend optimizations
- * disabled. As long as one entity wants it disabled, it should stay
- * that way
- *
- * @param enabled true if optimizations need enabled, false otherwise
- */
- public synchronized void setSuspendModeOptimizations(boolean enabled) {
-
- /* It is good to plumb suspend optimization enable
- * or disable even if ref count indicates already done
- * since we could have a case of previous failure.
- */
- if (!enabled) {
- mOptimizationsDisabledRefCount++;
- } else {
- mOptimizationsDisabledRefCount--;
- if (mOptimizationsDisabledRefCount > 0) {
- return;
- } else {
- /* Keep refcount from becoming negative */
- mOptimizationsDisabledRefCount = 0;
- }
- }
-
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
-
- WifiNative.setSuspendOptimizationsCommand(enabled);
- }
-
-
- /**
- * Set high performance mode of operation. This would mean
- * use active power mode and disable suspend optimizations
- * @param enabled true if enabled, false otherwise
- */
- public synchronized void setHighPerfMode(boolean enabled) {
- if (mIsHighPerfEnabled != enabled) {
- if (enabled) {
- setPowerMode(DRIVER_POWER_MODE_ACTIVE);
- setSuspendModeOptimizations(false);
- } else {
- setPowerMode(DRIVER_POWER_MODE_AUTO);
- setSuspendModeOptimizations(true);
- }
- mIsHighPerfEnabled = enabled;
- Log.d(TAG,"high performance mode: " + enabled);
- }
- }
-
-
- 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);
- }
-
- public void enableRssiPolling(boolean enable) {
- if (mEnableRssiPolling != enable) {
- mEnableRssiPolling = enable;
- checkPollTimer();
- }
- }
-
- /**
- * We release the wakelock in WifiService
- * using a timer.
- *
- * TODO:
- * Releasing wakelock using both timer and
- * a call from ConnectivityService requires
- * a rethink. We had problems where WifiService
- * could keep a wakelock forever if we delete
- * messages in the asynchronous call
- * from ConnectivityService
- */
- @Override
- public void releaseWakeLock() {
- }
-
- /**
- * Tracks the WPA supplicant states to detect "loop" situations.
- * @param newSupplicantState The new WPA supplicant state.
- * @return {@code true} if the supplicant loop should be stopped
- * and {@code false} if it should continue.
- */
- private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
- if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
- && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
- if (mSupplicantLoopState != newSupplicantState) {
- if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
- ++mNumSupplicantLoopIterations;
- }
-
- mSupplicantLoopState = newSupplicantState;
- }
- } else if (newSupplicantState == SupplicantState.COMPLETED) {
- resetSupplicantLoopState();
- }
-
- return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
- }
-
- /**
- * Resets the WPA supplicant loop state.
- */
- private void resetSupplicantLoopState() {
- mNumSupplicantLoopIterations = 0;
- }
-
- @Override
- public void handleMessage(Message msg) {
- Intent intent;
-
- switch (msg.what) {
- case EVENT_SUPPLICANT_CONNECTION:
- mRunState = RUN_STATE_RUNNING;
- String macaddr;
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- macaddr = WifiNative.getMacAddressCommand();
- }
- if (macaddr != null) {
- mWifiInfo.setMacAddress(macaddr);
- }
-
- checkUseStaticIp();
- /* Reset notification state on new connection */
- resetNotificationTimer();
- /*
- * DHCP requests are blocking, so run them in a separate thread.
- */
- HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
- dhcpThread.start();
- mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
- mIsScanModeActive = true;
- mIsHighPerfEnabled = false;
- mOptimizationsDisabledRefCount = 0;
- mPowerModeRefCount = 0;
- mTornDownByConnMgr = false;
- mLastBssid = null;
- mLastSsid = null;
- mIsAnyNetworkDisabled.set(false);
- requestConnectionInfo();
- SupplicantState supplState = mWifiInfo.getSupplicantState();
-
- if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
- supplState);
- // Wi-Fi supplicant connection state changed:
- // [31- 2] Reserved for future use
- // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
- // or supplicant died (2)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
- /*
- * The COMPLETED state change from the supplicant may have occurred
- * in between polling for supplicant availability, in which case
- * we didn't perform a DHCP request to get an IP address.
- */
- if (supplState == SupplicantState.COMPLETED) {
- mLastBssid = mWifiInfo.getBSSID();
- mLastSsid = mWifiInfo.getSSID();
- configureInterface();
- }
- if (ActivityManagerNative.isSystemReady()) {
- intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
- mContext.sendBroadcast(intent);
- }
- if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
- setDetailedState(DetailedState.CONNECTED);
- } else {
- setDetailedState(WifiInfo.getDetailedStateOf(supplState));
- }
- /*
- * Filter out multicast packets. This saves battery power, since
- * the CPU doesn't have to spend time processing packets that
- * are going to end up being thrown away.
- */
- mWM.initializeMulticastFiltering();
-
- if (mBluetoothA2dp == null) {
- mBluetoothA2dp = new BluetoothA2dp(mContext);
- }
- checkIsBluetoothPlaying();
-
- // initialize this after the supplicant is alive
- setNumAllowedChannels();
- break;
-
- case EVENT_SUPPLICANT_DISCONNECT:
- mRunState = RUN_STATE_STOPPED;
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- }
- boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
- mWifiState.get() != WIFI_STATE_DISABLING;
- if (died) {
- if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
- } else {
- if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
- }
- // Wi-Fi supplicant connection state changed:
- // [31- 2] Reserved for future use
- // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
- // or supplicant died (2)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0);
- closeSupplicantConnection();
-
- if (died) {
- resetConnections(true);
- }
- // When supplicant dies, kill the DHCP thread
- mDhcpTarget.getLooper().quit();
-
- mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
- if (ActivityManagerNative.isSystemReady()) {
- intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
- mContext.sendBroadcast(intent);
- }
- setDetailedState(DetailedState.DISCONNECTED);
- setSupplicantState(SupplicantState.UNINITIALIZED);
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- if (died) {
- mWM.setWifiEnabled(false);
- }
- break;
-
- case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
- // Only do this if we haven't gotten a new supplicant status since the timer
- // started
- if (mNumSupplicantStateChanges == msg.arg1) {
- scan(false); // do a passive scan
- }
- break;
-
- case EVENT_SUPPLICANT_STATE_CHANGED:
- mNumSupplicantStateChanges++;
- SupplicantStateChangeResult supplicantStateResult =
- (SupplicantStateChangeResult) msg.obj;
- SupplicantState newState = supplicantStateResult.state;
- SupplicantState currentState = mWifiInfo.getSupplicantState();
-
- // Wi-Fi supplicant state changed:
- // [31- 6] Reserved for future use
- // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
- int eventLogParam = (newState.ordinal() & 0x3f);
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
-
- if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
- + currentState +
- " ==> " + newState);
-
- int networkId = supplicantStateResult.networkId;
-
- /**
- * The SupplicantState BSSID value is valid in ASSOCIATING state only.
- * The NetworkState BSSID value comes upon a successful connection.
- */
- if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
- mLastBssid = supplicantStateResult.BSSID;
- }
- /*
- * If we get disconnect or inactive we need to start our
- * watchdog timer to start a scan
- */
- if (newState == SupplicantState.DISCONNECTED ||
- newState == SupplicantState.INACTIVE) {
- sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
- mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
- }
-
-
- /*
- * Did we get to DISCONNECTED state due to an
- * authentication (password) failure?
- */
- boolean failedToAuthenticate = false;
- if (newState == SupplicantState.DISCONNECTED) {
- failedToAuthenticate = mPasswordKeyMayBeIncorrect;
- }
- mPasswordKeyMayBeIncorrect = false;
-
- /*
- * Keep track of the supplicant state and check if we should
- * disable the network
- */
- boolean disabledNetwork = false;
- if (isSupplicantLooping(newState)) {
- if (LOCAL_LOGD) {
- Log.v(TAG,
- "Stop WPA supplicant loop and disable network");
- }
- disabledNetwork = wifiManagerDisableNetwork(networkId);
- }
-
- if (disabledNetwork) {
- /*
- * Reset the loop state if we disabled the network
- */
- resetSupplicantLoopState();
- } else if (newState != currentState ||
- (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
- setSupplicantState(newState);
- if (newState == SupplicantState.DORMANT) {
- DetailedState newDetailedState;
- Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
- if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
- newDetailedState = DetailedState.IDLE;
- } else {
- newDetailedState = DetailedState.FAILED;
- }
- handleDisconnectedState(newDetailedState, true);
- /**
- * We should never let the supplicant stay in DORMANT state
- * as long as we are in connect mode and driver is started
- *
- * We should normally hit a DORMANT state due to a disconnect
- * issued after an IP configuration failure. We issue a reconnect
- * after RECONNECT_DELAY_MSECS in such a case.
- *
- * After multiple failures, the network gets disabled and the
- * supplicant should reach an INACTIVE state.
- *
- */
- if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly) {
- sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
- } else if (mRunState == RUN_STATE_STOPPING) {
- stopDriver();
- } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
- reconnectCommand();
- }
- } else if (newState == SupplicantState.DISCONNECTED) {
- mHaveIpAddress = false;
- if (isDriverStopped() || mDisconnectExpected) {
- handleDisconnectedState(DetailedState.DISCONNECTED, true);
- } else {
- scheduleDisconnect();
- }
- } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
- /**
- * Ignore events that don't change the connectivity state,
- * such as WPA rekeying operations.
- */
- if (!(currentState == SupplicantState.COMPLETED &&
- (newState == SupplicantState.ASSOCIATING ||
- newState == SupplicantState.ASSOCIATED ||
- newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
- newState == SupplicantState.GROUP_HANDSHAKE))) {
- setDetailedState(WifiInfo.getDetailedStateOf(newState));
- }
- }
-
- mDisconnectExpected = false;
- 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)newState);
- if (failedToAuthenticate) {
- if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId);
- wifiManagerDisableNetwork(networkId);
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
- break;
-
- case EVENT_NETWORK_STATE_CHANGED:
- /*
- * Each CONNECT or DISCONNECT generates a pair of events.
- * One is a supplicant state change event, and the other
- * is a network state change event. For connects, the
- * supplicant event always arrives first, followed by
- * the network state change event. Only the latter event
- * has the BSSID, which we are interested in capturing.
- * For disconnects, the order is the opposite -- the
- * network state change event comes first, followed by
- * the supplicant state change event.
- */
- NetworkStateChangeResult result =
- (NetworkStateChangeResult) msg.obj;
-
- // Wi-Fi network state changed:
- // [31- 6] Reserved for future use
- // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
- eventLogParam = (result.state.ordinal() & 0x3f);
- EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
-
- if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
- /*
- * If we're in scan-only mode, don't advance the state machine, and
- * don't report the state change to clients.
- */
- if (mIsScanOnly) {
- if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
- break;
- }
- if (result.state != DetailedState.SCANNING) {
- /*
- * Reset the scan count since there was a network state
- * change. This could be from supplicant trying to associate
- * with a network.
- */
- mNumScansSinceNetworkStateChange = 0;
- }
- /*
- * If the supplicant sent us a CONNECTED event, we don't
- * want to send out an indication of overall network
- * connectivity until we have our IP address. If the
- * supplicant sent us a DISCONNECTED event, we delay
- * sending a notification in case a reconnection to
- * the same access point occurs within a short time.
- */
- if (result.state == DetailedState.DISCONNECTED) {
- if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
- scheduleDisconnect();
- }
- break;
- }
- requestConnectionStatus(mWifiInfo);
- if (!(result.state == DetailedState.CONNECTED &&
- (!mHaveIpAddress || mDisconnectPending))) {
- setDetailedState(result.state);
- }
-
- if (result.state == DetailedState.CONNECTED) {
- /*
- * Remove the 'available networks' notification when we
- * successfully connect to a network.
- */
- setNotificationVisible(false, 0, false, 0);
- boolean wasDisconnectPending = mDisconnectPending;
- cancelDisconnect();
- /*
- * The connection is fully configured as far as link-level
- * connectivity is concerned, but we may still need to obtain
- * an IP address.
- */
- if (wasDisconnectPending) {
- DetailedState saveState = getNetworkInfo().getDetailedState();
- handleDisconnectedState(DetailedState.DISCONNECTED, false);
- setDetailedStateInternal(saveState);
- }
-
- configureInterface();
- mLastBssid = result.BSSID;
- mLastSsid = mWifiInfo.getSSID();
- mLastNetworkId = result.networkId;
- if (mHaveIpAddress) {
- setDetailedState(DetailedState.CONNECTED);
- } else {
- setDetailedState(DetailedState.OBTAINING_IPADDR);
- }
- }
- sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
- break;
-
- case EVENT_SCAN_RESULTS_AVAILABLE:
- if (ActivityManagerNative.isSystemReady()) {
- mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
- sendScanResultsAvailable();
- /**
- * On receiving the first scan results after connecting to
- * the supplicant, switch scan mode over to passive.
- */
- setScanMode(false);
- break;
-
- case EVENT_POLL_INTERVAL:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- requestPolledInfo(mWifiInfo, true);
- checkPollTimer();
- }
- break;
-
- case EVENT_DEFERRED_DISCONNECT:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- handleDisconnectedState(DetailedState.DISCONNECTED, true);
- }
- break;
-
- case EVENT_DEFERRED_RECONNECT:
- /**
- * mLastBssid can be null when there is a reconnect
- * request on the first BSSID we connect to
- */
- String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
- /**
- * If we've exceeded the maximum number of retries for reconnecting
- * to a given network, disable the network
- */
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- if (++mReconnectCount > getMaxDhcpRetries()) {
- if (LOCAL_LOGD) {
- Log.d(TAG, "Failed reconnect count: " +
- mReconnectCount + " Disabling " + BSSID);
- }
- mWM.disableNetwork(mLastNetworkId);
- }
- reconnectCommand();
- }
- break;
-
- case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
- /**
- * Since this event is sent from another thread, it might have been
- * sent after we closed our connection to the supplicant in the course
- * of disabling Wi-Fi. In that case, we should just ignore the event.
- */
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- break;
- }
- mReconnectCount = 0;
- mHaveIpAddress = true;
- mObtainingIpAddress = false;
- mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
- mLastSignalLevel = -1; // force update of signal strength
- if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
- setDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
- } else {
- msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
- // Wi-Fi interface configuration state changed:
- // [31- 1] Reserved for future use
- // [ 0- 0] Interface configuration succeeded (1) or failed (0)
- EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
-
- // We've connected successfully, so allow the notification again in the future
- resetNotificationTimer();
- break;
-
- case EVENT_INTERFACE_CONFIGURATION_FAILED:
- if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- // Wi-Fi interface configuration state changed:
- // [31- 1] Reserved for future use
- // [ 0- 0] Interface configuration succeeded (1) or failed (0)
- EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
- mHaveIpAddress = false;
- mWifiInfo.setIpAddress(0);
- mObtainingIpAddress = false;
- disconnect();
- }
- break;
-
- case EVENT_DRIVER_STATE_CHANGED:
- // Wi-Fi driver state changed:
- // 0 STARTED
- // 1 STOPPED
- // 2 HUNG
- EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
-
- switch (msg.arg1) {
- case DRIVER_STARTED:
- /**
- * Set the number of allowed radio channels according
- * to the system setting, since it gets reset by the
- * driver upon changing to the STARTED state.
- */
- setNumAllowedChannels();
- synchronized (this) {
- macaddr = WifiNative.getMacAddressCommand();
- if (macaddr != null) {
- mWifiInfo.setMacAddress(macaddr);
- }
- mRunState = RUN_STATE_RUNNING;
- if (!mIsScanOnly) {
- reconnectCommand();
- } else {
- // In some situations, supplicant needs to be kickstarted to
- // start the background scanning
- scan(true);
- }
- }
- break;
- case DRIVER_STOPPED:
- mRunState = RUN_STATE_STOPPED;
- break;
- case DRIVER_HUNG:
- Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
- /**
- * restart the driver - toggle off and on
- */
- mWM.setWifiEnabled(false);
- mWM.setWifiEnabled(true);
- break;
- }
- synchronized (this) {
- updateBatteryWorkSourceLocked(null);
- }
- break;
-
- case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
- mPasswordKeyMayBeIncorrect = true;
- break;
- }
- }
-
- private boolean wifiManagerDisableNetwork(int networkId) {
- boolean disabledNetwork = false;
- if (0 <= networkId) {
- disabledNetwork = mWM.disableNetwork(networkId);
- if (LOCAL_LOGD) {
- if (disabledNetwork) {
- Log.v(TAG, "Disabled network: " + networkId);
- }
- }
- }
- if (LOCAL_LOGD) {
- if (!disabledNetwork) {
- Log.e(TAG, "Failed to disable network:" +
- " invalid network id: " + networkId);
- }
- }
- return disabledNetwork;
- }
-
- private void configureInterface() {
- checkPollTimer();
- mLastSignalLevel = -1;
- if (!mUseStaticIp) {
- if (!mHaveIpAddress && !mObtainingIpAddress) {
- mObtainingIpAddress = true;
- mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
- }
- } else {
- int event;
- if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- mHaveIpAddress = true;
- event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
- if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
- } else {
- mHaveIpAddress = false;
- event = EVENT_INTERFACE_CONFIGURATION_FAILED;
- if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
- }
- sendEmptyMessage(event);
- }
- }
-
- /**
- * Reset our IP state and send out broadcasts following a disconnect.
- * @param newState the {@code DetailedState} to set. Should be either
- * {@code DISCONNECTED} or {@code FAILED}.
- * @param disableInterface indicates whether the interface should
- * be disabled
- */
- private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
- if (mDisconnectPending) {
- cancelDisconnect();
- }
- mDisconnectExpected = false;
- resetConnections(disableInterface);
- setDetailedState(newState);
- sendNetworkStateChangeBroadcast(mLastBssid);
- mWifiInfo.setBSSID(null);
- mLastBssid = null;
- mLastSsid = null;
- mDisconnectPending = false;
- }
-
- /**
- * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
- * using the interface, stopping DHCP, and disabling the interface.
- */
- public void resetConnections(boolean disableInterface) {
- if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
- mHaveIpAddress = false;
- mObtainingIpAddress = false;
- mWifiInfo.setIpAddress(0);
-
- /*
- * Reset connection depends on both the interface and the IP assigned,
- * so it should be done before any chance of the IP being lost.
- */
- NetworkUtils.resetConnections(mInterfaceName);
-
- // Stop DHCP
- mDhcpTarget.setCancelCallback(true);
- mDhcpTarget.removeMessages(EVENT_DHCP_START);
-
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Could not stop DHCP");
- }
-
- /**
- * Interface is re-enabled in the supplicant
- * when moving out of ASSOCIATING state
- */
- if(disableInterface) {
- if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
- NetworkUtils.disableInterface(mInterfaceName);
- }
- }
-
- /**
- * The supplicant is reporting that we are disconnected from the current
- * access point. Often, however, a disconnect will be followed very shortly
- * by a reconnect to the same access point. Therefore, we delay resetting
- * the connection's IP state for a bit.
- */
- private void scheduleDisconnect() {
- mDisconnectPending = true;
- if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
- sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
- }
- }
-
- private void cancelDisconnect() {
- mDisconnectPending = false;
- removeMessages(EVENT_DEFERRED_DISCONNECT);
- }
-
- public DhcpInfo getDhcpInfo() {
- return mDhcpInfo;
- }
-
- public synchronized List<ScanResult> getScanResultsList() {
- return mScanResults;
- }
-
- public synchronized void setScanResultsList(List<ScanResult> scanList) {
- mScanResults = scanList;
- }
-
- /**
- * Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
- */
- public WifiInfo requestConnectionInfo() {
- requestConnectionStatus(mWifiInfo);
- requestPolledInfo(mWifiInfo, false);
- return mWifiInfo;
- }
-
- private void requestConnectionStatus(WifiInfo info) {
- String SSID = null;
- String BSSID = null;
- String suppState = null;
- int netId = -1;
- String reply = status();
- if (reply != null) {
- /*
- * Parse the reply from the supplicant to the status command, and update
- * local state accordingly. The reply is a series of lines of the form
- * "name=value".
- */
-
- String[] lines = reply.split("\n");
- for (String line : lines) {
- String[] prop = line.split(" *= *");
- if (prop.length < 2)
- continue;
- String name = prop[0];
- String value = prop[1];
- if (name.equalsIgnoreCase("id"))
- netId = Integer.parseInt(value);
- else if (name.equalsIgnoreCase("ssid"))
- SSID = value;
- else if (name.equalsIgnoreCase("bssid"))
- BSSID = value;
- else if (name.equalsIgnoreCase("wpa_state"))
- suppState = value;
- }
- }
- info.setNetworkId(netId);
- info.setSSID(SSID);
- info.setBSSID(BSSID);
- /*
- * We only set the supplicant state if the previous state was
- * UNINITIALIZED. This should only happen when we first connect to
- * the supplicant. Once we're connected, we should always receive
- * an event upon any state change, but in this case, we want to
- * make sure any listeners are made aware of the state change.
- */
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
- setSupplicantState(suppState);
- }
-
- /**
- * Get the dynamic information that is not reported via events.
- * @param info the object into which the information should be captured.
- */
- private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
- {
- int newRssi = (polling ? getRssiApprox() : getRssi());
- 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;
- info.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 informing 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 {
- info.setRssi(-200);
- }
- int newLinkSpeed = getLinkSpeed();
- if (newLinkSpeed != -1) {
- info.setLinkSpeed(newLinkSpeed);
- }
- }
-
- private void sendRssiChangeBroadcast(final int newRssi) {
- if (ActivityManagerNative.isSystemReady()) {
- Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
- }
- }
-
- private void sendNetworkStateChangeBroadcast(String bssid) {
- Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
- if (bssid != null)
- intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- mContext.sendStickyBroadcast(intent);
- }
-
- /**
- * Disable Wi-Fi connectivity by stopping the driver.
- */
- public boolean teardown() {
- if (!mTornDownByConnMgr) {
- if (disconnectAndStop()) {
- setTornDownByConnMgr(true);
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- /**
- * Reenable Wi-Fi connectivity by restarting the driver.
- */
- public boolean reconnect() {
- if (mTornDownByConnMgr) {
- if (restart()) {
- setTornDownByConnMgr(false);
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
- /**
- * We want to stop the driver, but if we're connected to a network,
- * we first want to disconnect, so that the supplicant is always in
- * a known state (DISCONNECTED) when the driver is stopped.
- * @return {@code true} if the operation succeeds, which means that the
- * disconnect or stop command was initiated.
- */
- public synchronized boolean disconnectAndStop() {
- boolean ret = true;;
- if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
- // Take down any open network notifications
- setNotificationVisible(false, 0, false, 0);
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
- ret = stopDriver();
- } else {
- ret = disconnect();
- }
- mRunState = RUN_STATE_STOPPING;
- }
- return ret;
- }
-
- public synchronized boolean restart() {
- if (isDriverStopped()) {
- mRunState = RUN_STATE_STARTING;
- resetConnections(true);
- return startDriver();
- }
- return true;
- }
-
- public int getWifiState() {
- return mWifiState.get();
- }
-
- public void setWifiState(int wifiState) {
- mWifiState.set(wifiState);
- }
-
- public boolean isAnyNetworkDisabled() {
- return mIsAnyNetworkDisabled.get();
- }
-
- /**
- * The WifiNative interface functions are listed below.
- * The only native call that is not synchronized on
- * WifiStateTracker is waitForEvent() which waits on a
- * seperate monitor channel.
- *
- * All supplicant commands need the wifi to be in an
- * enabled state. This can be done by checking the
- * mWifiState to be WIFI_STATE_ENABLED.
- *
- * All commands that can cause commands to driver
- * initiated need the driver state to be started.
- * This is done by checking isDriverStopped() to
- * be false.
- */
-
- /**
- * Load the driver and firmware
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean loadDriver() {
- return WifiNative.loadDriver();
- }
-
- /**
- * Unload the driver and firmware
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean unloadDriver() {
- return WifiNative.unloadDriver();
- }
-
- /**
- * Check the supplicant config and
- * start the supplicant daemon
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean startSupplicant() {
- return WifiNative.startSupplicant();
- }
-
- /**
- * Stop the supplicant daemon
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean stopSupplicant() {
- return WifiNative.stopSupplicant();
- }
-
- /**
- * Establishes two channels - control channel for commands
- * and monitor channel for notifying WifiMonitor
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean connectToSupplicant() {
- return WifiNative.connectToSupplicant();
- }
-
- /**
- * Close the control/monitor channels to supplicant
- */
- public synchronized void closeSupplicantConnection() {
- WifiNative.closeSupplicantConnection();
- }
-
- /**
- * Check if the supplicant is alive
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean ping() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.pingCommand();
- }
-
- /**
- * initiate an active or passive scan
- *
- * @param forceActive true if it is a active scan
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean scan(boolean forceActive) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.scanCommand(forceActive);
- }
-
- /**
- * Specifies whether the supplicant or driver
- * take care of initiating scan and doing AP selection
- *
- * @param mode
- * SUPPL_SCAN_HANDLING_NORMAL
- * SUPPL_SCAN_HANDLING_LIST_ONLY
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setScanResultHandling(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.setScanResultHandlingCommand(mode);
- }
-
- /**
- * Fetch the scan results from the supplicant
- *
- * @return example result string
- * 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
- */
- public synchronized String scanResults() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return null;
- }
- return WifiNative.scanResultsCommand();
- }
-
- /**
- * Set the scan mode - active or passive
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setScanMode(boolean isScanModeActive) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- if (mIsScanModeActive != isScanModeActive) {
- return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
- }
- return true;
- }
-
- /**
- * Disconnect from Access Point
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean disconnect() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.disconnectCommand();
- }
-
- /**
- * Initiate a reconnection to AP
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean reconnectCommand() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.reconnectCommand();
- }
-
- /**
- * Add a network
- *
- * @return network id of the new network
- */
- public synchronized int addNetwork() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return -1;
- }
- return WifiNative.addNetworkCommand();
- }
-
- /**
- * Delete a network
- *
- * @param networkId id of the network to be removed
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean removeNetwork(int networkId) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
- }
-
- /**
- * 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 synchronized boolean enableNetwork(int netId, boolean disableOthers) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- if (disableOthers) mIsAnyNetworkDisabled.set(true);
- return WifiNative.enableNetworkCommand(netId, disableOthers);
- }
-
- /**
- * Enable all networks
- *
- * @param networks list of configured networks
- */
- public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return;
- }
- mIsAnyNetworkDisabled.set(false);
- for (WifiConfiguration config : networks) {
- if (config.status == WifiConfiguration.Status.DISABLED) {
- WifiNative.enableNetworkCommand(config.networkId, false);
- }
- }
- }
-
- /**
- * Disable a network
- *
- * @param netId network id of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean disableNetwork(int netId) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- mIsAnyNetworkDisabled.set(true);
- return WifiNative.disableNetworkCommand(netId);
- }
-
- /**
- * Initiate a re-association in supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean reassociate() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.reassociateCommand();
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean addToBlacklist(String bssid) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.addToBlacklistCommand(bssid);
- }
-
- /**
- * Clear the blacklist list
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean clearBlacklist() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.clearBlacklistCommand();
- }
-
- /**
- * List all configured networks
- *
- * @return list of networks or null on failure
- */
- public synchronized String listNetworks() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.listNetworksCommand();
- }
-
- /**
- * Get network setting by name
- *
- * @param netId network id of the network
- * @param name network variable key
- * @return value corresponding to key
- */
- public synchronized String getNetworkVariable(int netId, String name) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.getNetworkVariableCommand(netId, name);
+ public boolean isAvailable() {
+ return mNetworkInfo.isAvailable();
}
/**
- * Set network setting by name
- *
- * @param netId network id of the network
- * @param name network variable key
- * @param value network variable value
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * @param enabled
*/
- public synchronized boolean setNetworkVariable(int netId, String name, String value) {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.setNetworkVariableCommand(netId, name, value);
+ public void setDataEnable(boolean enabled) {
+ android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
}
/**
- * 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
+ * Check if private DNS route is set for the network
*/
- public synchronized String status() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return null;
- }
- return WifiNative.statusCommand();
+ public boolean isPrivateDnsRouteSet() {
+ return mPrivateDnsRouteSet.get();
}
/**
- * Get RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
+ * Set a flag indicating private DNS route is set
*/
- public synchronized int getRssi() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getRssiApproxCommand();
+ public void privateDnsRouteSet(boolean enabled) {
+ mPrivateDnsRouteSet.set(enabled);
}
/**
- * Get approx RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
+ * Fetch NetworkInfo for the network
*/
- public synchronized int getRssiApprox() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getRssiApproxCommand();
+ public NetworkInfo getNetworkInfo() {
+ return mNetworkInfo;
}
/**
- * Get link speed to currently connected network
- *
- * @return link speed, -1 on failure
+ * Fetch LinkProperties for the network
*/
- public synchronized int getLinkSpeed() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getLinkSpeedCommand();
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
}
/**
- * Start driver
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
*
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * @return a copy of this connections capabilities, may be empty but never null.
*/
- public synchronized boolean startDriver() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.startDriverCommand();
+ public LinkCapabilities getLinkCapabilities() {
+ return new LinkCapabilities(mLinkCapabilities);
}
/**
- * Stop driver
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Check if default route is set
*/
- public synchronized boolean stopDriver() {
- /* Driver stop should not happen only when supplicant event
- * DRIVER_STOPPED has already been handled */
- if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
- return false;
- }
- return WifiNative.stopDriverCommand();
+ public boolean isDefaultRouteSet() {
+ return mDefaultRouteSet.get();
}
/**
- * Start packet filtering
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Set a flag indicating default route is set for the network
*/
- public synchronized boolean startPacketFiltering() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.startPacketFiltering();
+ public void defaultRouteSet(boolean enabled) {
+ mDefaultRouteSet.set(enabled);
}
/**
- * Stop packet filtering
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean stopPacketFiltering() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.stopPacketFiltering();
- }
-
- /**
- * Get power mode
- * @return power mode
+ * Return the system properties name associated with the tcp buffer sizes
+ * for this network.
*/
- public synchronized int getPowerMode() {
- if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
- return -1;
- }
- return WifiNative.getPowerModeCommand();
+ public String getTcpBufferSizesPropName() {
+ return "net.tcp.buffersize.wifi";
}
- /**
- * Set power mode
- * @param mode
- * DRIVER_POWER_MODE_AUTO
- * DRIVER_POWER_MODE_ACTIVE
- *
- * Uses reference counting to keep power mode active
- * as long as one entity wants power mode to be active.
- *
- * For example, WifiLock high perf mode can keep power mode active
- * or a DHCP session can keep it active. As long as one entity wants
- * it enabled, it should stay that way
- *
- */
- private synchronized void setPowerMode(int mode) {
-
- /* It is good to plumb power mode change
- * even if ref count indicates already done
- * since we could have a case of previous failure.
- */
- switch(mode) {
- case DRIVER_POWER_MODE_ACTIVE:
- mPowerModeRefCount++;
- break;
- case DRIVER_POWER_MODE_AUTO:
- mPowerModeRefCount--;
- if (mPowerModeRefCount > 0) {
+ private class WifiStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ mLinkProperties = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ if (mLinkProperties == null) {
+ mLinkProperties = new LinkProperties();
+ }
+ mLinkCapabilities = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_CAPABILITIES);
+ if (mLinkCapabilities == null) {
+ mLinkCapabilities = new LinkCapabilities();
+ }
+ // don't want to send redundent state messages
+ // TODO can this be fixed in WifiStateMachine?
+ NetworkInfo.State state = mNetworkInfo.getState();
+ if (mLastState == state) {
return;
} else {
- /* Keep refcount from becoming negative */
- mPowerModeRefCount = 0;
+ mLastState = state;
}
- break;
- }
-
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
-
- WifiNative.setPowerModeCommand(mode);
- }
-
- /**
- * Set the number of allowed radio frequency channels from the system
- * setting value, if any.
- * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
- * the number of channels is invalid.
- */
- public synchronized boolean setNumAllowedChannels() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- try {
- return setNumAllowedChannels(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
- } catch (Settings.SettingNotFoundException e) {
- if (mNumAllowedChannels != 0) {
- WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
+ Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
+ } else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) {
+ mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
}
- // otherwise, use the driver default
- }
- return true;
- }
-
- /**
- * 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.
- * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
- * {@code numChannels} is outside the valid range.
- */
- public synchronized boolean setNumAllowedChannels(int numChannels) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- mNumAllowedChannels = numChannels;
- return WifiNative.setNumAllowedChannelsCommand(numChannels);
- }
-
- /**
- * Get number of allowed channels
- *
- * @return channel count, -1 on failure
- */
- public synchronized int getNumAllowedChannels() {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return -1;
- }
- return WifiNative.getNumAllowedChannelsCommand();
- }
-
- /**
- * Set bluetooth coex mode:
- *
- * @param mode
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean setBluetoothCoexistenceMode(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
- }
- return WifiNative.setBluetoothCoexistenceModeCommand(mode);
- }
-
- /**
- * 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 synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return;
- }
- WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
- }
-
- /**
- * Save configuration on supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean saveConfig() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
- }
- return WifiNative.saveConfigCommand();
- }
-
- /**
- * Reload the configuration from file
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public synchronized boolean reloadConfig() {
- if (mWifiState.get() != WIFI_STATE_ENABLED) {
- return false;
}
- return WifiNative.reloadConfigCommand();
}
- public boolean setRadio(boolean turnOn) {
- return mWM.setWifiEnabled(turnOn);
- }
-
- /**
- * {@inheritDoc}
- * There are currently no Wi-Fi-specific features supported.
- * @param feature the name of the feature
- * @return {@code -1} indicating failure, always
- */
- public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- return -1;
- }
-
- /**
- * {@inheritDoc}
- * There are currently no Wi-Fi-specific features supported.
- * @param feature the name of the feature
- * @return {@code -1} indicating failure, always
- */
- public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- return -1;
- }
-
- @Override
- public void interpretScanResultsAvailable() {
-
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
-
- NetworkInfo networkInfo = getNetworkInfo();
-
- State state = networkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
-
- // Look for an open network
- List<ScanResult> scanResults = getScanResultsList();
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- if (TextUtils.isEmpty(scanResult.capabilities)) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification mainly so we can remove the
- // EVENT_NOTIFICATION_CHANGED message with this Notification from
- // the queue later
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
- ICON_NETWORKS_AVAILABLE, mNotification);
-
- } else {
-
- // Remove any pending messages to show the notification
- mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-
- message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
- }
-
- mTarget.sendMessageDelayed(message, delay);
-
- mNotificationShown = visible;
- }
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently.
- * <p>
- * After calling this method, the timer that prevents notifications from
- * being shown too often will be cleared.
- */
- private void resetNotificationTimer() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("interface ").append(mInterfaceName);
- sb.append(" runState=");
- if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
- sb.append(mRunStateNames[mRunState-1]);
- } else {
- sb.append(mRunState);
- }
- sb.append(LS).append(mWifiInfo).append(LS);
- sb.append(mDhcpInfo).append(LS);
- sb.append("haveIpAddress=").append(mHaveIpAddress).
- append(", obtainingIpAddress=").append(mObtainingIpAddress).
- append(", scanModeActive=").append(mIsScanModeActive).append(LS).
- append("lastSignalLevel=").append(mLastSignalLevel).
- append(", explicitlyDisabled=").append(mTornDownByConnMgr);
- return sb.toString();
- }
-
- private class DhcpHandler extends Handler {
-
- private Handler mTarget;
-
- /**
- * Whether to skip the DHCP result callback to the target. For example,
- * this could be set if the network we were requesting an IP for has
- * since been disconnected.
- * <p>
- * Note: There is still a chance where the client's intended DHCP
- * request not being canceled. For example, we are request for IP on
- * A, and he queues request for IP on B, and then cancels the request on
- * B while we're still requesting from A.
- */
- private boolean mCancelCallback;
-
- /**
- * 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;
-
- public DhcpHandler(Looper looper, Handler target) {
- super(looper);
- mTarget = target;
-
- mBluetoothHeadset = new BluetoothHeadset(mContext, null);
- }
-
- public void handleMessage(Message msg) {
- int event;
-
- switch (msg.what) {
- case EVENT_DHCP_START:
-
- boolean modifiedBluetoothCoexistenceMode = false;
- int 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
- setBluetoothCoexistenceMode(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
- }
-
- powerMode = getPowerMode();
- if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
- powerMode = DRIVER_POWER_MODE_AUTO;
- }
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- setPowerMode(DRIVER_POWER_MODE_ACTIVE);
- }
-
- synchronized (this) {
- // A new request is being made, so assume we will callback
- mCancelCallback = false;
- }
- Log.d(TAG, "DhcpHandler: DHCP request started");
- if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
- event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
- if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
- } else {
- event = EVENT_INTERFACE_CONFIGURATION_FAILED;
- Log.i(TAG, "DhcpHandler: DHCP request failed: " +
- NetworkUtils.getDhcpError());
- }
-
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- setPowerMode(powerMode);
- }
-
- if (modifiedBluetoothCoexistenceMode) {
- // Set the coexistence mode back to its default value
- setBluetoothCoexistenceMode(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
- }
-
- synchronized (this) {
- if (!mCancelCallback) {
- mTarget.sendEmptyMessage(event);
- }
- }
- break;
- }
- }
-
- public synchronized void setCancelCallback(boolean cancelCallback) {
- mCancelCallback = cancelCallback;
- }
-
- /**
- * 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 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);
- }
-
- 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) {
- resetConnections(true);
- configureInterface();
- if (mUseStaticIp) {
- Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
- }
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
-
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- mNotificationEnabled = getValue();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- mNotificationEnabled = getValue();
- if (!mNotificationEnabled) {
- // Remove any notification that may be showing
- setNotificationVisible(false, 0, true, 0);
- }
-
- resetNotificationTimer();
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
+ public void setDependencyMet(boolean met) {
+ // not supported on this network
}
}
diff --git a/wifi/java/android/net/wifi/WpsConfiguration.aidl b/wifi/java/android/net/wifi/WpsConfiguration.aidl
new file mode 100644
index 0000000..6c26833
--- /dev/null
+++ b/wifi/java/android/net/wifi/WpsConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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;
+
+parcelable WpsConfiguration;
diff --git a/wifi/java/android/net/wifi/WpsConfiguration.java b/wifi/java/android/net/wifi/WpsConfiguration.java
new file mode 100644
index 0000000..12d951f
--- /dev/null
+++ b/wifi/java/android/net/wifi/WpsConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.LinkProperties;
+import android.net.wifi.WifiConfiguration.IpAssignment;
+import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.BitSet;
+
+/**
+ * A class representing a WPS network configuration
+ * @hide
+ */
+public class WpsConfiguration implements Parcelable {
+
+ public enum Setup {
+ /* Wi-Fi protected setup push button configuration */
+ PBC,
+ /* Wi-Fi protected setup pin method configuration with pin obtained from access point */
+ PIN_FROM_ACCESS_POINT,
+ /* Wi-Fi protected setup pin method configuration with pin obtained from device */
+ PIN_FROM_DEVICE,
+ /* Invalid config */
+ INVALID
+ }
+
+ public Setup setup;
+
+ public String BSSID;
+
+ public String pin;
+
+ public IpAssignment ipAssignment;
+
+ public ProxySettings proxySettings;
+
+ public LinkProperties linkProperties;
+
+ public WpsConfiguration() {
+ setup = Setup.INVALID;
+ BSSID = null;
+ pin = null;
+ ipAssignment = IpAssignment.UNASSIGNED;
+ proxySettings = ProxySettings.UNASSIGNED;
+ linkProperties = new LinkProperties();
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append(" setup: ").append(setup.toString());
+ sbuf.append('\n');
+ sbuf.append(" BSSID: ").append(BSSID);
+ sbuf.append('\n');
+ sbuf.append(" pin: ").append(pin);
+ sbuf.append('\n');
+ sbuf.append("IP assignment: " + ipAssignment.toString());
+ sbuf.append("\n");
+ sbuf.append("Proxy settings: " + proxySettings.toString());
+ sbuf.append("\n");
+ sbuf.append(linkProperties.toString());
+ sbuf.append("\n");
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ public WpsConfiguration(WpsConfiguration source) {
+ if (source != null) {
+ setup = source.setup;
+ BSSID = source.BSSID;
+ pin = source.pin;
+ ipAssignment = source.ipAssignment;
+ proxySettings = source.proxySettings;
+ linkProperties = new LinkProperties(source.linkProperties);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(setup.name());
+ dest.writeString(BSSID);
+ dest.writeString(pin);
+ dest.writeString(ipAssignment.name());
+ dest.writeString(proxySettings.name());
+ dest.writeParcelable(linkProperties, flags);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WpsConfiguration> CREATOR =
+ new Creator<WpsConfiguration>() {
+ public WpsConfiguration createFromParcel(Parcel in) {
+ WpsConfiguration config = new WpsConfiguration();
+ config.setup = Setup.valueOf(in.readString());
+ config.BSSID = in.readString();
+ config.pin = in.readString();
+ config.ipAssignment = IpAssignment.valueOf(in.readString());
+ config.proxySettings = ProxySettings.valueOf(in.readString());
+ config.linkProperties = in.readParcelable(null);
+ return config;
+ }
+
+ public WpsConfiguration[] newArray(int size) {
+ return new WpsConfiguration[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WpsResult.aidl b/wifi/java/android/net/wifi/WpsResult.aidl
new file mode 100644
index 0000000..eb4c4f5
--- /dev/null
+++ b/wifi/java/android/net/wifi/WpsResult.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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;
+
+parcelable WpsResult;
diff --git a/wifi/java/android/net/wifi/WpsResult.java b/wifi/java/android/net/wifi/WpsResult.java
new file mode 100644
index 0000000..d4fd3e2
--- /dev/null
+++ b/wifi/java/android/net/wifi/WpsResult.java
@@ -0,0 +1,90 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing the result of a WPS request
+ * @hide
+ */
+public class WpsResult implements Parcelable {
+
+ public enum Status {
+ SUCCESS,
+ FAILURE,
+ IN_PROGRESS,
+ }
+
+ public Status status;
+
+ public String pin;
+
+ public WpsResult() {
+ status = Status.FAILURE;
+ pin = null;
+ }
+
+ public WpsResult(Status s) {
+ status = s;
+ pin = null;
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append(" status: ").append(status.toString());
+ sbuf.append('\n');
+ sbuf.append(" pin: ").append(pin);
+ sbuf.append("\n");
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ public WpsResult(WpsResult source) {
+ if (source != null) {
+ status = source.status;
+ pin = source.pin;
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(status.name());
+ dest.writeString(pin);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WpsResult> CREATOR =
+ new Creator<WpsResult>() {
+ public WpsResult createFromParcel(Parcel in) {
+ WpsResult result = new WpsResult();
+ result.status = Status.valueOf(in.readString());
+ result.pin = in.readString();
+ return result;
+ }
+
+ public WpsResult[] newArray(int size) {
+ return new WpsResult[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java
new file mode 100644
index 0000000..32d77a1
--- /dev/null
+++ b/wifi/java/android/net/wifi/WpsStateMachine.java
@@ -0,0 +1,206 @@
+/*
+ * 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 com.android.internal.util.AsyncChannel;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiStateMachine.StateChangeResult;
+import android.net.wifi.WpsResult.Status;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Manages a WPS connection.
+ *
+ * WPS consists as a series of EAP exchange triggered
+ * by a user action that leads to a successful connection
+ * after automatic creation of configuration in the
+ * supplicant. We currently support the following methods
+ * of WPS setup
+ * 1. Pin method: Pin can be either obtained from the device
+ * or from the access point to connect to.
+ * 2. Push button method: This involves pushing a button on
+ * the access point and the device
+ *
+ * After a successful WPS setup, the state machine
+ * reloads the configuration and updates the IP and proxy
+ * settings, if any.
+ */
+class WpsStateMachine extends HierarchicalStateMachine {
+
+ private static final String TAG = "WpsStateMachine";
+ private static final boolean DBG = false;
+
+ private WifiStateMachine mWifiStateMachine;
+
+ private WpsConfiguration mWpsConfig;
+
+ private Context mContext;
+ AsyncChannel mReplyChannel = new AsyncChannel();
+
+ private HierarchicalState mDefaultState = new DefaultState();
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mActiveState = new ActiveState();
+
+ public WpsStateMachine(Context context, WifiStateMachine wsm, Handler target) {
+ super(TAG, target.getLooper());
+
+ mContext = context;
+ mWifiStateMachine = wsm;
+ addState(mDefaultState);
+ addState(mInactiveState, mDefaultState);
+ addState(mActiveState, mDefaultState);
+
+ setInitialState(mInactiveState);
+
+ //start the state machine
+ start();
+ }
+
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState 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");
+ WpsConfiguration wpsConfig;
+ switch (message.what) {
+ case WifiStateMachine.CMD_START_WPS:
+ mWpsConfig = (WpsConfiguration) message.obj;
+ WpsResult result;
+ switch (mWpsConfig.setup) {
+ case PBC:
+ result = WifiConfigStore.startWpsPbc(mWpsConfig);
+ break;
+ case PIN_FROM_ACCESS_POINT:
+ result = WifiConfigStore.startWpsWithPinFromAccessPoint(mWpsConfig);
+ break;
+ case PIN_FROM_DEVICE:
+ result = WifiConfigStore.startWpsWithPinFromDevice(mWpsConfig);
+ break;
+ default:
+ result = new WpsResult(Status.FAILURE);
+ Log.e(TAG, "Invalid setup for WPS");
+ break;
+ }
+ mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED, result);
+ if (result.status == Status.SUCCESS) {
+ transitionTo(mActiveState);
+ } else {
+ Log.e(TAG, "Failed to start WPS with config " + mWpsConfig.toString());
+ }
+ break;
+ case WifiStateMachine.CMD_RESET_WPS_STATE:
+ transitionTo(mInactiveState);
+ break;
+ default:
+ Log.e(TAG, "Failed to handle " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class ActiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ boolean retValue = HANDLED;
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+ switch (supState) {
+ case COMPLETED:
+ /* During WPS setup, all other networks are disabled. After
+ * a successful connect a new config is created in the supplicant.
+ *
+ * We need to enable all networks after a successful connection
+ * and the configuration list needs to be reloaded from the supplicant.
+ */
+ Log.d(TAG, "WPS set up successful");
+ WifiConfigStore.enableAllNetworks();
+ WifiConfigStore.loadConfiguredNetworks();
+ WifiConfigStore.updateIpAndProxyFromWpsConfig(
+ stateChangeResult.networkId, mWpsConfig);
+ mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
+ transitionTo(mInactiveState);
+ break;
+ case INACTIVE:
+ /* A failed WPS connection */
+ Log.d(TAG, "WPS set up failed, enabling other networks");
+ WifiConfigStore.enableAllNetworks();
+ mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
+ transitionTo(mInactiveState);
+ break;
+ default:
+ if (DBG) Log.d(TAG, "Ignoring supplicant state " + supState.name());
+ break;
+ }
+ break;
+ case WifiStateMachine.CMD_START_WPS:
+ /* Ignore request and send an in progress message */
+ mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
+ new WpsResult(Status.IN_PROGRESS));
+ break;
+ default:
+ retValue = NOT_HANDLED;
+ }
+ return retValue;
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ boolean retValue = HANDLED;
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ //Ignore supplicant state changes
+ case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ break;
+ default:
+ retValue = NOT_HANDLED;
+ }
+ return retValue;
+ }
+ }
+
+}