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