blob: 9003706868f2da6bb9b2df21947b485b1b394004 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.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:
* Deprecate WIFI_STATE_UNKNOWN
*/
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.backup.IBackupManager;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.DhcpStateMachine;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.TrafficStats;
import android.net.wifi.*;
import android.net.wifi.SupplicantState;
import android.net.wifi.WpsResult.Status;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.LruCache;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.NetlinkTracker;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Inet4Address;
/**
* Track the state of Wifi connectivity. All event handling is done here,
* and all changes in connectivity state are initiated here.
*
* Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
* In the current implementation, we support concurrent wifi p2p and wifi operation.
* The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
* handles p2p operation.
*
* @hide
*/
public class WifiStateMachine extends StateMachine {
private static final String NETWORKTYPE = "WIFI";
private static boolean DBG = false;
private static boolean VDBG = false;
private static boolean VVDBG = false;
private static boolean mLogMessages = false;
private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
private static final String GOOGLE_OUI = "DA-A1-19";
/* temporary debug flag - best network selection development */
private static boolean PDBG = false;
/* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
* the corresponding BSSID.
*/
private boolean didBlackListBSSID = false;
/**
* Log with error attribute
*
* @param s is string log
*/
protected void loge(String s) {
Log.e(getName(), s);
}
protected void log(String s) {;
Log.e(getName(), s);
}
private WifiMonitor mWifiMonitor;
private WifiNative mWifiNative;
private WifiConfigStore mWifiConfigStore;
private WifiAutoJoinController mWifiAutoJoinController;
private INetworkManagementService mNwService;
private ConnectivityManager mCm;
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
private boolean mTemporarilyDisconnectWifi = false;
private final String mPrimaryDeviceType;
/* Scan results handling */
private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
private static final Pattern scanResultPattern = Pattern.compile("\t+");
private static final int SCAN_RESULT_CACHE_SIZE = 160;
private final LruCache<String, ScanResult> mScanResultCache;
// For debug, number of known scan results that were found as part of last scan result event,
// as well the number of scans results returned by the supplicant with that message
private int mNumScanResultsKnown;
private int mNumScanResultsReturned;
/* Batch scan results */
private final List<BatchedScanResult> mBatchedScanResults =
new ArrayList<BatchedScanResult>();
private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
private int mExpectedBatchedScans = 0;
private long mBatchedScanMinPollTime = 0;
private boolean mScreenOn = false;
/* Chipset supports background scan */
private final boolean mBackgroundScanSupported;
private String mInterfaceName;
/* Tethering interface could be separate from wlan interface */
private String mTetherInterfaceName;
private int mLastSignalLevel = -1;
private String mLastBssid;
private int mLastNetworkId; // The network Id we successfully joined
private boolean linkDebouncing = false;
// Testing various network disconnect cases by sending lots of spurious
// disconnect to supplicant
private boolean testNetworkDisconnect = false;
private boolean mEnableRssiPolling = false;
private boolean mEnableBackgroundScan = false;
private int mRssiPollToken = 0;
/* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
* In CONNECT_MODE, the STA can scan and connect to an access point
* In SCAN_ONLY_MODE, the STA can only scan for access points
* In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
*/
private int mOperationalMode = CONNECT_MODE;
private boolean mIsScanOngoing = false;
private boolean mIsFullScanOngoing = false;
private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
private WorkSource mScanWorkSource = null;
private static final int UNKNOWN_SCAN_SOURCE = -1;
private static final int SCAN_ALARM_SOURCE = -2;
private static final int ADD_OR_UPDATE_SOURCE = -3;
private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
private static final String SCAN_REQUEST_TIME = "scan_request_time";
private static final String BATCHED_SETTING = "batched_settings";
private static final String BATCHED_WORKSOURCE = "batched_worksource";
/* Tracks if state machine has received any screen state change broadcast yet.
* We can miss one of these at boot.
*/
private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
private boolean mBluetoothConnectionActive = false;
private PowerManager.WakeLock mSuspendWakeLock;
/**
* Interval in milliseconds between polling for RSSI
* and linkspeed information
*/
private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
/**
* Interval in milliseconds between receiving a disconnect event
* while connected to a good AP, and handling the disconnect proper
*/
private static final int LINK_FLAPPING_DEBOUNCE_MSEC = 7000;
/**
* Delay between supplicant restarts upon failure to establish connection
*/
private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
/**
* Number of times we attempt to restart supplicant
*/
private static final int SUPPLICANT_RESTART_TRIES = 5;
private int mSupplicantRestartCount = 0;
/* Tracks sequence number on stop failure message */
private int mSupplicantStopFailureToken = 0;
/**
* Tether state change notification time out
*/
private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
/* Tracks sequence number on a tether notification time out */
private int mTetherToken = 0;
/**
* Driver start time out.
*/
private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
/* Tracks sequence number on a driver time out */
private int mDriverStartToken = 0;
/**
* The link properties of the wifi interface.
* Do not modify this directly; use updateLinkProperties instead.
*/
private LinkProperties mLinkProperties;
/* Tracks sequence number on a periodic scan message */
private int mPeriodicScanToken = 0;
// Wakelock held during wifi start/stop and driver load/unload
private PowerManager.WakeLock mWakeLock;
private Context mContext;
private final Object mDhcpResultsLock = new Object();
private DhcpResults mDhcpResults;
private WifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private NetworkCapabilities mNetworkCapabilities;
private SupplicantStateTracker mSupplicantStateTracker;
private DhcpStateMachine mDhcpStateMachine;
private boolean mDhcpActive = false;
private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
// Whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
private int mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
// Roaming failure count
private int mRoamFailCount = 0;
// This is the BSSID we are trying to associate to, it can be set to "any"
// if we havent selected a BSSID for joining.
// The BSSID we are associated to is found in mWifiInfo
private String mTargetRoamBSSID = "any";
private long mLastDriverRoamAttempt = 0;
private WifiConfiguration targetWificonfiguration = null;
// Used as debug to indicate which configuration last was saved
private WifiConfiguration lastSavedConfigurationAttempt = null;
// Used as debug to indicate which configuration last was removed
private WifiConfiguration lastForgetConfigurationAttempt = null;
boolean isRoaming() {
return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
|| mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
}
public void autoRoamSetBSSID(int netId, String bssid) {
autoRoamSetBSSID(mWifiConfigStore.getWifiConfiguration(netId), bssid);
}
public boolean autoRoamSetBSSID(WifiConfiguration config, String bssid) {
boolean ret = true;
if (mTargetRoamBSSID == null) mTargetRoamBSSID = "any";
if (bssid == null) bssid = "any";
if (config == null) return false; // Nothing to do
if (mTargetRoamBSSID != null && bssid == mTargetRoamBSSID && bssid == config.BSSID) {
return false; // We didnt change anything
}
if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
// Changing to ANY
if (!mWifiConfigStore.roamOnAny) {
ret = false; // Nothing to do
}
}
if (VDBG) {
loge("autoRoamSetBSSID " + bssid
+ " key=" + config.configKey());
}
config.autoJoinBSSID = bssid;
mTargetRoamBSSID = bssid;
mWifiConfigStore.saveWifiConfigBSSID(config);
return ret;
}
/**
* Subset of link properties coming from netlink.
* Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
* and domains obtained from router advertisements (RFC 6106).
*/
private NetlinkTracker mNetlinkTracker;
private AlarmManager mAlarmManager;
private PendingIntent mScanIntent;
private PendingIntent mDriverStopIntent;
private PendingIntent mBatchedScanIntervalIntent;
/* Tracks current frequency mode */
private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
/* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
// Channel for sending replies.
private AsyncChannel mReplyChannel = new AsyncChannel();
private WifiP2pServiceImpl mWifiP2pServiceImpl;
// Used to initiate a connection with WifiP2pService
private AsyncChannel mWifiP2pChannel;
private AsyncChannel mWifiApConfigChannel;
private WifiNetworkFactory mNetworkFactory;
private WifiNetworkAgent mNetworkAgent;
// Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
// We should really persist that into the networkHistory.txt file, and read it back when
// WifiStateMachine starts up
private WifiConnectionStatistics mWifiConnectionStatistics = new WifiConnectionStatistics();
// Used to filter out requests we couldn't possibly satisfy.
private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
/* The base for wifi message types */
static final int BASE = Protocol.BASE_WIFI;
/* Start the supplicant */
static final int CMD_START_SUPPLICANT = BASE + 11;
/* Stop the supplicant */
static final int CMD_STOP_SUPPLICANT = BASE + 12;
/* Start the driver */
static final int CMD_START_DRIVER = BASE + 13;
/* Stop the driver */
static final int CMD_STOP_DRIVER = BASE + 14;
/* Indicates Static IP succeeded */
static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
/* Indicates Static IP failed */
static final int CMD_STATIC_IP_FAILURE = BASE + 16;
/* Indicates supplicant stop failed */
static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
/* Delayed stop to avoid shutting down driver too quick*/
static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
/* A delayed message sent to start driver when it fail to come up */
static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
/* Start the soft access point */
static final int CMD_START_AP = BASE + 21;
/* Indicates soft ap start succeeded */
static final int CMD_START_AP_SUCCESS = BASE + 22;
/* Indicates soft ap start failed */
static final int CMD_START_AP_FAILURE = BASE + 23;
/* Stop the soft access point */
static final int CMD_STOP_AP = BASE + 24;
/* Set the soft access point configuration */
static final int CMD_SET_AP_CONFIG = BASE + 25;
/* Soft access point configuration set completed */
static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26;
/* Request the soft access point configuration */
static final int CMD_REQUEST_AP_CONFIG = BASE + 27;
/* Response to access point configuration request */
static final int CMD_RESPONSE_AP_CONFIG = BASE + 28;
/* Invoked when getting a tether state change notification */
static final int CMD_TETHER_STATE_CHANGE = BASE + 29;
/* A delayed message sent to indicate tether state change failed to arrive */
static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30;
static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
/* Supplicant commands */
/* Is supplicant alive ? */
static final int CMD_PING_SUPPLICANT = BASE + 51;
/* Add/update a network configuration */
static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52;
/* Delete a network */
static final int CMD_REMOVE_NETWORK = BASE + 53;
/* Enable a network. The device will attempt a connection to the given network. */
static final int CMD_ENABLE_NETWORK = BASE + 54;
/* Enable all networks */
static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
/* Blacklist network. De-prioritizes the given BSSID for connection. */
static final int CMD_BLACKLIST_NETWORK = BASE + 56;
/* Clear the blacklist network list */
static final int CMD_CLEAR_BLACKLIST = BASE + 57;
/* Save configuration */
static final int CMD_SAVE_CONFIG = BASE + 58;
/* Get configured networks */
static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
/* Get available frequencies */
static final int CMD_GET_CAPABILITY_FREQ = BASE + 60;
/* Get adaptors */
static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61;
/* Get configured networks with real preSharedKey */
static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
/* Get Link Layer Stats thru HAL */
static final int CMD_GET_LINK_LAYER_STATS = BASE + 63;
/* Supplicant commands after driver start*/
/* Initiate a scan */
static final int CMD_START_SCAN = BASE + 71;
/* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
static final int CMD_SET_OPERATIONAL_MODE = BASE + 72;
/* Disconnect from a network */
static final int CMD_DISCONNECT = BASE + 73;
/* Reconnect to a network */
static final int CMD_RECONNECT = BASE + 74;
/* Reassociate to a network */
static final int CMD_REASSOCIATE = BASE + 75;
/* Get Connection Statistis */
static final int CMD_GET_CONNECTION_STATISTICS = BASE + 76;
/* Controls suspend mode optimizations
*
* When high perf mode is enabled, suspend mode optimizations are disabled
*
* When high perf mode is disabled, suspend mode optimizations are enabled
*
* Suspend mode optimizations include:
* - packet filtering
* - turn off roaming
* - DTIM wake up settings
*/
static final int CMD_SET_HIGH_PERF_MODE = BASE + 77;
/* Set the country code */
static final int CMD_SET_COUNTRY_CODE = BASE + 80;
/* Enables RSSI poll */
static final int CMD_ENABLE_RSSI_POLL = BASE + 82;
/* RSSI poll */
static final int CMD_RSSI_POLL = BASE + 83;
/* Set up packet filtering */
static final int CMD_START_PACKET_FILTERING = BASE + 84;
/* Clear packet filter */
static final int CMD_STOP_PACKET_FILTERING = BASE + 85;
/* Enable suspend mode optimizations in the driver */
static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86;
/* Delayed NETWORK_DISCONNECT */
static final int CMD_DELAYED_NETWORK_DISCONNECT = BASE + 87;
/* When there are no saved networks, we do a periodic scan to notify user of
* an open network */
static final int CMD_NO_NETWORKS_PERIODIC_SCAN = BASE + 88;
/* Test network Disconnection NETWORK_DISCONNECT */
static final int CMD_TEST_NETWORK_DISCONNECT = BASE + 89;
private int testNetworkDisconnectCounter = 0;
/* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
static final int MULTICAST_V6 = 1;
static final int MULTICAST_V4 = 0;
/* Set the frequency band */
static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
/* Enable TDLS on a specific MAC address */
static final int CMD_ENABLE_TDLS = BASE + 92;
/* DHCP/IP configuration watchdog */
static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER = BASE + 93;
/**
* Make this timer 40 seconds, which is about the normal DHCP timeout.
* In no valid case, the WiFiStateMachine should remain stuck in ObtainingIpAddress
* for more than 30 seconds.
*/
static final int OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC = 40000;
int obtainingIpWatchdogCount = 0;
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
/**
* Watchdog for protecting against b/16823537
* Leave time for 4-ways handshake to succeed
*/
static final int ROAM_GUARD_TIMER_MSEC = 15000;
int roamWatchdogCount = 0;
/* Roam state watchdog */
static final int CMD_ROAM_WATCHDOG_TIMER = BASE + 94;
/* Screen change intent handling */
static final int CMD_SCREEN_STATE_CHANGED = BASE + 95;
int disconnectingWatchdogCount = 0;
static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
/* Disconnecting state watchdog */
static final int CMD_DISCONNECTING_WATCHDOG_TIMER = BASE + 96;
/* P2p commands */
/* We are ok with no response here since we wont do much with it anyway */
public static final int CMD_ENABLE_P2P = BASE + 131;
/* In order to shut down supplicant cleanly, we wait till p2p has
* been disabled */
public static final int CMD_DISABLE_P2P_REQ = BASE + 132;
public static final int CMD_DISABLE_P2P_RSP = BASE + 133;
public static final int CMD_BOOT_COMPLETED = BASE + 134;
/* change the batch scan settings.
* arg1 = responsible UID
* arg2 = csph (channel scans per hour)
* obj = bundle with the new settings and the optional worksource
*/
public static final int CMD_SET_BATCHED_SCAN = BASE + 135;
public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136;
public static final int CMD_POLL_BATCHED_SCAN = BASE + 137;
/* We now have a valid IP configuration. */
static final int CMD_IP_CONFIGURATION_SUCCESSFUL = BASE + 138;
/* We no longer have a valid IP configuration. */
static final int CMD_IP_CONFIGURATION_LOST = BASE + 139;
/* Link configuration (IP address, DNS, ...) changes notified via netlink */
static final int CMD_UPDATE_LINKPROPERTIES = BASE + 140;
/* Supplicant is trying to associate to a given BSSID */
static final int CMD_TARGET_BSSID = BASE + 141;
/* Reload all networks and reconnect */
static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
static final int CMD_AUTO_CONNECT = BASE + 143;
static final int network_status_unwanted_disconnect = 0;
static final int network_status_unwanted_disable_autojoin = 1;
static final int CMD_UNWANTED_NETWORK = BASE + 144;
static final int CMD_AUTO_ROAM = BASE + 145;
static final int CMD_AUTO_SAVE_NETWORK = BASE + 146;
static final int CMD_ASSOCIATED_BSSID = BASE + 147;
/* Wifi state machine modes of operation */
/* CONNECT_MODE - connect to any 'known' AP when it becomes available */
public static final int CONNECT_MODE = 1;
/* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
public static final int SCAN_ONLY_MODE = 2;
/* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
private static final int SUCCESS = 1;
private static final int FAILURE = -1;
/* Tracks if suspend optimizations need to be disabled by DHCP,
* screen or due to high perf mode.
* When any of them needs to disable it, we keep the suspend optimizations
* disabled
*/
private int mSuspendOptNeedsDisabled = 0;
private static final int SUSPEND_DUE_TO_DHCP = 1;
private static final int SUSPEND_DUE_TO_HIGH_PERF = 1<<1;
private static final int SUSPEND_DUE_TO_SCREEN = 1<<2;
/* Tracks if user has enabled suspend optimizations through settings */
private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
/**
* Default framework scan interval in milliseconds. This is used in the scenario in which
* wifi chipset does not support background scanning to set up a
* periodic wake up scan so that the device can connect to a new access
* point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
* override this.
*/
private final int mDefaultFrameworkScanIntervalMs;
private int mDisconnectedScanPeriodMs = 10000;
/**
* Supplicant scan interval in milliseconds.
* Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
* from the default config if the setting is not set
*/
private long mSupplicantScanIntervalMs;
/**
* timeStamp of last full band scan we perfoemed for autojoin while connected with screen lit
*/
private long lastFullBandConnectedTimeMilli;
/**
* time interval to the next full band scan we will perform for
* autojoin while connected with screen lit
*/
private long fullBandConnectedTimeIntervalMilli;
/**
* max time interval to the next full band scan we will perform for
* autojoin while connected with screen lit
* Max time is 5 minutes
*/
private static final long maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
/**
* Minimum time interval between enabling all networks.
* A device can end up repeatedly connecting to a bad network on screen on/off toggle
* due to enabling every time. We add a threshold to avoid this.
*/
private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
private long mLastEnableAllNetworksTime;
int mRunningBeaconCount = 0;
/**
* Starting and shutting down driver too quick causes problems leading to driver
* being in a bad state. Delay driver stop.
*/
private final int mDriverStopDelayMs;
private int mDelayedStopCounter;
private boolean mInDelayedStop = false;
// sometimes telephony gives us this data before boot is complete and we can't store it
// until after, so the write is deferred
private volatile String mPersistedCountryCode;
// Supplicant doesn't like setting the same country code multiple times (it may drop
// currently connected network), so we save the country code here to avoid redundency
private String mLastSetCountryCode;
/* Default parent state */
private State mDefaultState = new DefaultState();
/* Temporary initial state */
private State mInitialState = new InitialState();
/* Driver loaded, waiting for supplicant to start */
private State mSupplicantStartingState = new SupplicantStartingState();
/* Driver loaded and supplicant ready */
private State mSupplicantStartedState = new SupplicantStartedState();
/* Waiting for supplicant to stop and monitor to exit */
private State mSupplicantStoppingState = new SupplicantStoppingState();
/* Driver start issued, waiting for completed event */
private State mDriverStartingState = new DriverStartingState();
/* Driver started */
private State mDriverStartedState = new DriverStartedState();
/* Wait until p2p is disabled
* This is a special state which is entered right after we exit out of DriverStartedState
* before transitioning to another state.
*/
private State mWaitForP2pDisableState = new WaitForP2pDisableState();
/* Driver stopping */
private State mDriverStoppingState = new DriverStoppingState();
/* Driver stopped */
private State mDriverStoppedState = new DriverStoppedState();
/* Scan for networks, no connection will be established */
private State mScanModeState = new ScanModeState();
/* Connecting to an access point */
private State mConnectModeState = new ConnectModeState();
/* Connected at 802.11 (L2) level */
private State mL2ConnectedState = new L2ConnectedState();
/* fetching IP after connection to access point (assoc+auth complete) */
private State mObtainingIpState = new ObtainingIpState();
/* Waiting for link quality verification to be complete */
private State mVerifyingLinkState = new VerifyingLinkState();
/* Connected with IP addr */
private State mConnectedState = new ConnectedState();
/* Roaming */
private State mRoamingState = new RoamingState();
/* disconnect issued, waiting for network disconnect confirmation */
private State mDisconnectingState = new DisconnectingState();
/* Network is not connected, supplicant assoc+auth is not complete */
private State mDisconnectedState = new DisconnectedState();
/* Waiting for WPS to be completed*/
private State mWpsRunningState = new WpsRunningState();
/* Soft ap is starting up */
private State mSoftApStartingState = new SoftApStartingState();
/* Soft ap is running */
private State mSoftApStartedState = new SoftApStartedState();
/* Soft ap is running and we are waiting for tether notification */
private State mTetheringState = new TetheringState();
/* Soft ap is running and we are tethered through connectivity service */
private State mTetheredState = new TetheredState();
/* Waiting for untether confirmation before stopping soft Ap */
private State mUntetheringState = new UntetheringState();
private class TetherStateChange {
ArrayList<String> available;
ArrayList<String> active;
TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
available = av;
active = ac;
}
}
public static class SimAuthRequestData {
int networkId;
int protocol;
String ssid;
String[] challenges;
}
public static class SimAuthResponseData {
int id;
String Kc1;
String SRES1;
String Kc2;
String SRES2;
String Kc3;
String SRES3;
}
/**
* 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 static final int SCAN_REQUEST = 0;
private static final String ACTION_START_SCAN =
"com.android.server.WifiManager.action.START_SCAN";
private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
private static final int DRIVER_STOP_REQUEST = 0;
private static final String ACTION_DELAYED_DRIVER_STOP =
"com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
private static final String ACTION_REFRESH_BATCHED_SCAN =
"com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
/**
* Keep track of whether WIFI is running.
*/
private boolean mIsRunning = false;
/**
* Keep track of whether we last told the battery stats we had started.
*/
private boolean mReportedRunning = false;
/**
* Most recently set source of starting WIFI.
*/
private final WorkSource mRunningWifiUids = new WorkSource();
/**
* The last reported UIDs that were responsible for starting WIFI.
*/
private final WorkSource mLastRunningWifiUids = new WorkSource();
private final IBatteryStats mBatteryStats;
private BatchedScanSettings mBatchedScanSettings = null;
/**
* Track the worksource/cost of the current settings and track what's been noted
* to the battery stats, so we can mark the end of the previous when changing.
*/
private WorkSource mBatchedScanWorkSource = null;
private int mBatchedScanCsph = 0;
private WorkSource mNotedBatchedScanWorkSource = null;
private int mNotedBatchedScanCsph = 0;
private String mTcpBufferSizes = null;
// Used for debug and stats gathering
private static int sScanAlarmIntentCount = 0;
public WifiStateMachine(Context context, String wlanInterface,
WifiTrafficPoller trafficPoller){
super("WifiStateMachine");
mContext = context;
mInterfaceName = wlanInterface;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT);
mWifiNative = new WifiNative(mInterfaceName);
mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
mWifiAutoJoinController = new WifiAutoJoinController(context, this,
mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
mWifiMonitor = new WifiMonitor(this, mWifiNative);
mWifiInfo = new WifiInfo();
mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
getHandler());
mLinkProperties = new LinkProperties();
IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
mNetlinkTracker = new NetlinkTracker(mInterfaceName, new NetlinkTracker.Callback() {
public void update() {
sendMessage(CMD_UPDATE_LINKPROPERTIES);
}
});
try {
mNwService.registerObserver(mNetlinkTracker);
} catch (RemoteException e) {
loge("Couldn't register netlink tracker: " + e.toString());
}
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Intent scanIntent = new Intent(ACTION_START_SCAN, null);
mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
R.integer.config_wifi_framework_scan_interval);
mDriverStopDelayMs = mContext.getResources().getInteger(
R.integer.config_wifi_driver_stop_delay);
mBackgroundScanSupported = mContext.getResources().getBoolean(
R.bool.config_wifi_background_scan_support);
mPrimaryDeviceType = mContext.getResources().getString(
R.string.config_wifi_p2p_device_type);
mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
// TODO - needs to be a bit more dynamic
mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ArrayList<String> available = intent.getStringArrayListExtra(
ConnectivityManager.EXTRA_AVAILABLE_TETHER);
ArrayList<String> active = intent.getStringArrayListExtra(
ConnectivityManager.EXTRA_ACTIVE_TETHER);
sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
sScanAlarmIntentCount++;
startScan(SCAN_ALARM_SOURCE, -2, null, null);
if (VDBG)
loge("WiFiStateMachine SCAN ALARM");
}
},
new IntentFilter(ACTION_START_SCAN));
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_ON)) {
sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
} else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
startNextBatchedScanAsync();
}
}
}, filter);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
}
},
new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
new ContentObserver(getHandler()) {
@Override
public void onChange(boolean selfChange) {
mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
}
});
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
sendMessage(CMD_BOOT_COMPLETED);
}
},
new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
mSuspendWakeLock.setReferenceCounted(false);
mTcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_wifi_tcp_buffers);
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mSupplicantStartingState, mDefaultState);
addState(mSupplicantStartedState, mDefaultState);
addState(mDriverStartingState, mSupplicantStartedState);
addState(mDriverStartedState, mSupplicantStartedState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mVerifyingLinkState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApStartingState, mDefaultState);
addState(mSoftApStartedState, mDefaultState);
addState(mTetheringState, mSoftApStartedState);
addState(mTetheredState, mSoftApStartedState);
addState(mUntetheringState, mSoftApStartedState);
setInitialState(mInitialState);
setLogRecSize(3000);
setLogOnlyTransitions(false);
if (VDBG) setDbg(true);
//start the state machine
start();
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private int mVerboseLoggingLevel = 0;
int getVerboseLoggingLevel() {
return mVerboseLoggingLevel;
}
void enableVerboseLogging(int verbose) {
mVerboseLoggingLevel = verbose;
if (verbose > 0) {
DBG = true;
VDBG = true;
PDBG = true;
mLogMessages = true;
mWifiNative.setSupplicantLogLevel("DEBUG");
} else {
DBG = false;
VDBG = false;
PDBG = false;
mLogMessages = false;
mWifiNative.setSupplicantLogLevel("INFO");
}
mWifiAutoJoinController.enableVerboseLogging(verbose);
mWifiMonitor.enableVerboseLogging(verbose);
mWifiNative.enableVerboseLogging(verbose);
mWifiConfigStore.enableVerboseLogging(verbose);
mSupplicantStateTracker.enableVerboseLogging(verbose);
}
private int mAggressiveHandover = 0;
int getAggressiveHandover() {
return mAggressiveHandover;
}
void enableAggressiveHandover(int enabled) {
mAggressiveHandover = enabled;
}
public void setAllowScansWithTraffic(int enabled) {
mWifiConfigStore.alwaysEnableScansWhileAssociated = enabled;
}
public int getAllowScansWithTraffic() {
return mWifiConfigStore.alwaysEnableScansWhileAssociated;
}
/*
*
* Framework scan control
*/
private boolean mAlarmEnabled = false;
/* This is set from the overlay config file or from a secure setting.
* A value of 0 disables scanning in the framework.
*/
private long mFrameworkScanIntervalMs = 10000;
private long mCurrentScanAlarmMs = 10000;
private void setScanAlarm(boolean enabled, int delayMilli) {
if (PDBG) {
loge("setScanAlarm " + enabled
+ " period " + mCurrentScanAlarmMs
+ " initial delay " + delayMilli);
}
if (mCurrentScanAlarmMs <= 0) enabled = false;
if (enabled == mAlarmEnabled) return;
if (enabled) {
long initialDelayMilli;
if (delayMilli <= 0) {
// scan now
startScan(SCAN_ALARM_SOURCE, 0, null, null);
initialDelayMilli = mCurrentScanAlarmMs;
} else {
initialDelayMilli = delayMilli;
}
int type = AlarmManager.RTC;
/* Set RTC_WAKEUP alarms if PNO is not supported - because no one is */
/* going to wake up the host processor to look for access points */
if (mBackgroundScanSupported == false)
type = AlarmManager.RTC_WAKEUP;
mAlarmManager.setRepeating(type,
System.currentTimeMillis() + initialDelayMilli,
mCurrentScanAlarmMs,
mScanIntent);
mAlarmEnabled = true;
} else {
mAlarmManager.cancel(mScanIntent);
mAlarmEnabled = false;
}
}
private boolean setRandomMacOui() {
String oui = mContext.getResources().getString(
R.string.config_wifi_random_mac_oui, GOOGLE_OUI);
String[] ouiParts = oui.split("-");
byte[] ouiBytes = new byte[3];
ouiBytes[0] = (byte) (Integer.parseInt(ouiParts[0], 16) & 0xFF);
ouiBytes[1] = (byte) (Integer.parseInt(ouiParts[1], 16) & 0xFF);
ouiBytes[2] = (byte) (Integer.parseInt(ouiParts[2], 16) & 0xFF);
logd("Setting OUI to " + oui);
return mWifiNative.setScanningMacOui(ouiBytes);
}
/*********************************************************
* Methods exposed for public use
********************************************************/
public Messenger getMessenger() {
return new Messenger(getHandler());
}
public WifiMonitor getWifiMonitor() {
return mWifiMonitor;
}
/**
* TODO: doc
*/
public boolean syncPingSupplicant(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
boolean result = (resultMsg.arg1 != FAILURE);
resultMsg.recycle();
return result;
}
public List<WifiChannel> syncGetChannelList(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ);
List<WifiChannel> list = null;
if (resultMsg.obj != null) {
list = new ArrayList<WifiChannel>();
String freqs = (String) resultMsg.obj;
String[] lines = freqs.split("\n");
for (String line : lines)
if (line.contains("MHz")) {
// line format: " 52 = 5260 MHz (NO_IBSS) (DFS)"
WifiChannel c = new WifiChannel();
String[] prop = line.split(" ");
if (prop.length < 5) continue;
try {
c.channelNum = Integer.parseInt(prop[1]);
c.freqMHz = Integer.parseInt(prop[3]);
} catch (NumberFormatException e) { }
c.isDFS = line.contains("(DFS)");
list.add(c);
} else if (line.contains("Mode[B] Channels:")) {
// B channels are the same as G channels, skipped
break;
}
}
resultMsg.recycle();
return (list != null && list.size() > 0) ? list : null;
}
/**
* Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
* given to callingUid.
*
* @param callingUid The uid initiating the wifi scan. Blame will be given here unless
* workSource is specified.
* @param workSource If not null, blame is given to workSource.
* @param settings Scan settings, see {@link ScanSettings}.
*/
public void startScan(int callingUid, int scanCounter,
ScanSettings settings, WorkSource workSource) {
Bundle bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
}
/**
* start or stop batched scanning using the given settings
*/
public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
WorkSource workSource) {
Bundle bundle = new Bundle();
bundle.putParcelable(BATCHED_SETTING, settings);
bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
}
public List<BatchedScanResult> syncGetBatchedScanResultsList() {
synchronized (mBatchedScanResults) {
List<BatchedScanResult> batchedScanList =
new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
for(BatchedScanResult result: mBatchedScanResults) {
batchedScanList.add(new BatchedScanResult(result));
}
return batchedScanList;
}
}
public void requestBatchedScanPoll() {
sendMessage(CMD_POLL_BATCHED_SCAN);
}
private void startBatchedScan() {
if (mBatchedScanSettings == null) return;
if (mDhcpActive) {
if (DBG) log("not starting Batched Scans due to DHCP");
return;
}
// first grab any existing data
retrieveBatchedScanData();
if (PDBG) loge("try starting Batched Scans due to DHCP");
mAlarmManager.cancel(mBatchedScanIntervalIntent);
String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
try {
mExpectedBatchedScans = Integer.parseInt(scansExpected);
setNextBatchedAlarm(mExpectedBatchedScans);
if (mExpectedBatchedScans > 0) noteBatchedScanStart();
} catch (NumberFormatException e) {
stopBatchedScan();
loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
}
}
// called from BroadcastListener
private void startNextBatchedScanAsync() {
sendMessage(CMD_START_NEXT_BATCHED_SCAN);
}
private void startNextBatchedScan() {
// first grab any existing data
retrieveBatchedScanData();
setNextBatchedAlarm(mExpectedBatchedScans);
}
private void handleBatchedScanPollRequest() {
if (DBG) {
log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
mBatchedScanSettings);
}
// if there is no appropriate PollTime that's because we either aren't
// batching or we've already set a time for a poll request
if (mBatchedScanMinPollTime == 0) return;
if (mBatchedScanSettings == null) return;
long now = System.currentTimeMillis();
if (now > mBatchedScanMinPollTime) {
// do the poll and reset our timers
startNextBatchedScan();
} else {
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
mBatchedScanIntervalIntent);
mBatchedScanMinPollTime = 0;
}
}
// return true if new/different
private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
if (DBG) {
log("set batched scan to " + settings + " for uid=" + responsibleUid +
", worksource=" + responsibleWorkSource);
}
if (settings != null) {
if (settings.equals(mBatchedScanSettings)) return false;
} else {
if (mBatchedScanSettings == null) return false;
}
mBatchedScanSettings = settings;
if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
mBatchedScanWorkSource = responsibleWorkSource;
mBatchedScanCsph = csph;
return true;
}
private void stopBatchedScan() {
mAlarmManager.cancel(mBatchedScanIntervalIntent);
retrieveBatchedScanData();
mWifiNative.setBatchedScanSettings(null);
noteBatchedScanStop();
}
private void setNextBatchedAlarm(int scansExpected) {
if (mBatchedScanSettings == null || scansExpected < 1) return;
mBatchedScanMinPollTime = System.currentTimeMillis() +
mBatchedScanSettings.scanIntervalSec * 1000;
if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
scansExpected = mBatchedScanSettings.maxScansPerBatch;
}
int secToFull = mBatchedScanSettings.scanIntervalSec;
secToFull *= scansExpected;
int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
if (debugPeriod > 0) secToFull = debugPeriod;
// set the alarm to do the next poll. We set it a little short as we'd rather
// wake up wearly than miss a scan due to buffer overflow
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
mBatchedScanIntervalIntent);
}
/**
* Start reading new scan data
* Data comes in as:
* "scancount=5\n"
* "nextcount=5\n"
* "apcount=3\n"
* "trunc\n" (optional)
* "bssid=...\n"
* "ssid=...\n"
* "freq=...\n" (in Mhz)
* "level=...\n"
* "dist=...\n" (in cm)
* "distsd=...\n" (standard deviation, in cm)
* "===="
* "bssid=...\n"
* etc
* "===="
* "bssid=...\n"
* etc
* "%%%%"
* "apcount=2\n"
* "bssid=...\n"
* etc
* "%%%%
* etc
* "----"
*/
private final static boolean DEBUG_PARSE = false;
private void retrieveBatchedScanData() {
String rawData = mWifiNative.getBatchedScanResults();
if (DEBUG_PARSE) log("rawData = " + rawData);
mBatchedScanMinPollTime = 0;
if (rawData == null || rawData.equalsIgnoreCase("OK")) {
loge("Unexpected BatchedScanResults :" + rawData);
return;
}
int scanCount = 0;
final String END_OF_BATCHES = "----";
final String SCANCOUNT = "scancount=";
final String TRUNCATED = "trunc";
final String AGE = "age=";
final String DIST = "dist=";
final String DISTSD = "distSd=";
String splitData[] = rawData.split("\n");
int n = 0;
if (splitData[n].startsWith(SCANCOUNT)) {
try {
scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
} catch (NumberFormatException e) {
loge("scancount parseInt Exception from " + splitData[n]);
}
} else log("scancount not found");
if (scanCount == 0) {
loge("scanCount==0 - aborting");
return;
}
final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
synchronized (mBatchedScanResults) {
mBatchedScanResults.clear();
BatchedScanResult batchedScanResult = new BatchedScanResult();
String bssid = null;
WifiSsid wifiSsid = null;
int level = 0;
int freq = 0;
int dist, distSd;
long tsf = 0;
dist = distSd = ScanResult.UNSPECIFIED;
final long now = SystemClock.elapsedRealtime();
final int bssidStrLen = BSSID_STR.length();
while (true) {
while (n < splitData.length) {
if (DEBUG_PARSE) logd("parsing " + splitData[n]);
if (splitData[n].equals(END_OF_BATCHES)) {
if (n+1 != splitData.length) {
loge("didn't consume " + (splitData.length-n));
}
if (mBatchedScanResults.size() > 0) {
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
logd("retrieveBatchedScanResults X");
return;
}
if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
if (bssid != null) {
batchedScanResult.scanResults.add(new ScanResult(
wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
wifiSsid = null;
bssid = null;
level = 0;
freq = 0;
tsf = 0;
dist = distSd = ScanResult.UNSPECIFIED;
}
if (splitData[n].equals(END_STR)) {
if (batchedScanResult.scanResults.size() != 0) {
mBatchedScanResults.add(batchedScanResult);
batchedScanResult = new BatchedScanResult();
} else {
logd("Found empty batch");
}
}
} else if (splitData[n].equals(TRUNCATED)) {
batchedScanResult.truncated = true;
} else if (splitData[n].startsWith(BSSID_STR)) {
bssid = new String(splitData[n].getBytes(), bssidStrLen,
splitData[n].length() - bssidStrLen);
} else if (splitData[n].startsWith(FREQ_STR)) {
try {
freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
} catch (NumberFormatException e) {
loge("Invalid freqency: " + splitData[n]);
freq = 0;
}
} else if (splitData[n].startsWith(AGE)) {
try {
tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
tsf *= 1000; // convert mS -> uS
} catch (NumberFormatException e) {
loge("Invalid timestamp: " + splitData[n]);
tsf = 0;
}
} else if (splitData[n].startsWith(SSID_STR)) {
wifiSsid = WifiSsid.createFromAsciiEncoded(
splitData[n].substring(SSID_STR.length()));
} else if (splitData[n].startsWith(LEVEL_STR)) {
try {
level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
if (level > 0) level -= 256;
} catch (NumberFormatException e) {
loge("Invalid level: " + splitData[n]);
level = 0;
}
} else if (splitData[n].startsWith(DIST)) {
try {
dist = Integer.parseInt(splitData[n].substring(DIST.length()));
} catch (NumberFormatException e) {
loge("Invalid distance: " + splitData[n]);
dist = ScanResult.UNSPECIFIED;
}
} else if (splitData[n].startsWith(DISTSD)) {
try {
distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
} catch (NumberFormatException e) {
loge("Invalid distanceSd: " + splitData[n]);
distSd = ScanResult.UNSPECIFIED;
}
} else {
loge("Unable to parse batched scan result line: " + splitData[n]);
}
n++;
}
rawData = mWifiNative.getBatchedScanResults();
if (DEBUG_PARSE) log("reading more data:\n" + rawData);
if (rawData == null) {
loge("Unexpected null BatchedScanResults");
return;
}
splitData = rawData.split("\n");
if (splitData.length == 0 || splitData[0].equals("ok")) {
loge("batch scan results just ended!");
if (mBatchedScanResults.size() > 0) {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
return;
}
n = 0;
}
}
}
private long mDisconnectedTimeStamp = 0;
public long getDisconnectedTimeMilli() {
if (getCurrentState() == mDisconnectedState
&& mDisconnectedTimeStamp != 0) {
long now_ms = System.currentTimeMillis();
return now_ms - mDisconnectedTimeStamp;
}
return 0;
}
// Keeping track of scan requests
private long lastStartScanTimeStamp = 0;
private long lastScanDuration = 0;
// Last connect attempt is used to prevent scan requests:
// - for a period of 10 seconds after attempting to connect
private long lastConnectAttempt = 0;
private String lastScanFreqs = null;
// For debugging, keep track of last message status handling
// TODO, find an equivalent mechanism as part of parent class
private static int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
private static int MESSAGE_HANDLING_STATUS_OK = 1;
private static int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
private static int MESSAGE_HANDLING_STATUS_REFUSED = -1;
private static int MESSAGE_HANDLING_STATUS_FAIL = -2;
private static int MESSAGE_HANDLING_STATUS_BUFFERED = -3;
private static int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
private static int MESSAGE_HANDLING_STATUS_DISCARD = -5;
private static int MESSAGE_HANDLING_STATUS_LOOPED = -6;
private static int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
private int messageHandlingStatus = 0;
//TODO: this is used only to track connection attempts, however the link state and packet per
//TODO: second logic should be folded into that
private boolean isScanAllowed() {
long now = System.currentTimeMillis();
if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) {
return false;
}
return true;
}
private int mOnTime = 0;
private int mTxTime = 0;
private int mRxTime = 0;
private int mOnTimeStartScan = 0;
private int mTxTimeStartScan = 0;
private int mRxTimeStartScan = 0;
private int mOnTimeScan = 0;
private int mTxTimeScan = 0;
private int mRxTimeScan = 0;
private int mOnTimeThisScan = 0;
private int mTxTimeThisScan = 0;
private int mRxTimeThisScan = 0;
private int mOnTimeScreenStateChange = 0;
private int mOnTimeAtLastReport = 0;
private long lastOntimeReportTimeStamp = 0;
private long lastScreenStateChangeTimeStamp = 0;
private int mOnTimeLastReport = 0;
private int mTxTimeLastReport = 0;
private int mRxTimeLastReport = 0;
private long lastLinkLayerStatsUpdate = 0;
String reportOnTime() {
long now = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
// Report stats since last report
int on = mOnTime - mOnTimeLastReport;
mOnTimeLastReport = mOnTime;
int tx = mTxTime - mTxTimeLastReport;
mTxTimeLastReport = mTxTime;
int rx = mRxTime - mRxTimeLastReport;
mRxTimeLastReport = mRxTime;
int period = (int)(now - lastOntimeReportTimeStamp);
lastOntimeReportTimeStamp = now;
sb.append(String.format("[on:%d tx:%d rx:%d period:%d]", on, tx, rx, period));
// Report stats since Screen State Changed
on = mOnTime - mOnTimeScreenStateChange;
period = (int)(now - lastScreenStateChangeTimeStamp);
sb.append(String.format(" from screen [on:%d period:%d]", on, period));
return sb.toString();
}
WifiLinkLayerStats getWifiLinkLayerStats(boolean dbg) {
WifiLinkLayerStats stats = null;
if (mWifiLinkLayerStatsSupported > 0) {
String name = "wlan0";
stats = mWifiNative.getWifiLinkLayerStats(name);
if (name != null && stats == null && mWifiLinkLayerStatsSupported > 0) {
mWifiLinkLayerStatsSupported -= 1;
} else if (stats != null) {
lastLinkLayerStatsUpdate = System.currentTimeMillis();
mOnTime = stats.on_time;
mTxTime = stats.tx_time;
mRxTime = stats.rx_time;
mRunningBeaconCount = stats.beacon_rx;
if (dbg) {
loge("WifiLinkLayerStats:");
loge(stats.toString());
}
}
}
return stats;
}
void startRadioScanStats() {
WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
if (stats != null) {
mOnTimeStartScan = stats.on_time;
mTxTimeStartScan = stats.tx_time;
mRxTimeStartScan = stats.rx_time;
mOnTime = stats.on_time;
mTxTime = stats.tx_time;
mRxTime = stats.rx_time;
}
}
void closeRadioScanStats() {
WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
if (stats != null) {
mOnTimeThisScan = stats.on_time - mOnTimeStartScan;
mTxTimeThisScan = stats.tx_time - mTxTimeStartScan;
mRxTimeThisScan = stats.rx_time - mRxTimeStartScan;
mOnTimeScan += mOnTimeThisScan;
mTxTimeScan += mTxTimeThisScan;
mRxTimeScan += mRxTimeThisScan;
}
}
// If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
private void noteScanStart(int callingUid, WorkSource workSource) {
long now = System.currentTimeMillis();
lastStartScanTimeStamp = now;
lastScanDuration = 0;
if (DBG) {
String ts = String.format("[%,d ms]", now);
if (workSource != null) {
loge(ts + " noteScanStart" + workSource.toString()
+ " uid " + Integer.toString(callingUid));
} else {
loge(ts + " noteScanstart no scan source");
}
}
startRadioScanStats();
if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE
&& callingUid != SCAN_ALARM_SOURCE)
|| workSource != null)) {
mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
try {
mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
} catch (RemoteException e) {
log(e.toString());
}
}
}
private void noteScanEnd() {
long now = System.currentTimeMillis();
if (lastStartScanTimeStamp != 0) {
lastScanDuration = now - lastStartScanTimeStamp;
}
lastStartScanTimeStamp = 0;
if (DBG) {
String ts = String.format("[%,d ms]", now);
if (mScanWorkSource != null)
loge(ts + " noteScanEnd " + mScanWorkSource.toString());
else
loge(ts + " noteScanEnd no scan source");
}
if (mScanWorkSource != null) {
try {
mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
} catch (RemoteException e) {
log(e.toString());
} finally {
mScanWorkSource = null;
}
}
}
private void noteBatchedScanStart() {
if (PDBG) loge("noteBatchedScanstart()");
// note the end of a previous scan set
if (mNotedBatchedScanWorkSource != null &&
(mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
mNotedBatchedScanCsph != mBatchedScanCsph)) {
try {
mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
} catch (RemoteException e) {
log(e.toString());
} finally {
mNotedBatchedScanWorkSource = null;
mNotedBatchedScanCsph = 0;
}
}
// note the start of the new
try {
mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
mBatchedScanCsph);
mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
mNotedBatchedScanCsph = mBatchedScanCsph;
} catch (RemoteException e) {
log(e.toString());
}
}
private void noteBatchedScanStop() {
if (PDBG) loge("noteBatchedScanstop()");
if (mNotedBatchedScanWorkSource != null) {
try {
mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
} catch (RemoteException e) {
log(e.toString());
} finally {
mNotedBatchedScanWorkSource = null;
mNotedBatchedScanCsph = 0;
}
}
}
private void handleScanRequest(int type, Message message) {
// unbundle parameters
Bundle bundle = (Bundle) message.obj;
ScanSettings settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
WorkSource workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
// parse scan settings
String freqs = null;
if (settings != null && settings.channelSet != null) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (WifiChannel channel : settings.channelSet) {
if (!first) sb.append(','); else first = false;
sb.append(channel.freqMHz);
}
freqs = sb.toString();
}
// call wifi native to start the scan
if (startScanNative(type, freqs)) {
// only count battery consumption if scan request is accepted
noteScanStart(message.arg1, workSource);
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
return;
}
// if reach here, scan request is rejected
if (!mIsScanOngoing) {
// if rejection is NOT due to ongoing scan (e.g. bad scan parameters),
// discard this request and pop up the next one
if (mBufferedScanMsg.size() > 0) {
sendMessage(mBufferedScanMsg.remove());
}
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
} else if (!mIsFullScanOngoing) {
// if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
// buffer the scan request to make sure specified channels will be scanned eventually
if (freqs == null)
mBufferedScanMsg.clear();
if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
Message msg = obtainMessage(CMD_START_SCAN,
message.arg1, message.arg2, bundle);
mBufferedScanMsg.add(msg);
} else {
// if too many requests in buffer, combine them into a single full scan
bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle);
mBufferedScanMsg.clear();
mBufferedScanMsg.add(msg);
}
messageHandlingStatus = MESSAGE_HANDLING_STATUS_LOOPED;
} else {
// mIsScanOngoing and mIsFullScanOngoing
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
}
}
/** return true iff scan request is accepted */
private boolean startScanNative(int type, String freqs) {
if (mWifiNative.scan(type, freqs)) {
mIsScanOngoing = true;
mIsFullScanOngoing = (freqs == null);
lastScanFreqs = freqs;
return true;
}
return false;
}
/**
* TODO: doc
*/
public void setSupplicantRunning(boolean enable) {
if (enable) {
sendMessage(CMD_START_SUPPLICANT);
} else {
sendMessage(CMD_STOP_SUPPLICANT);
}
}
/**
* TODO: doc
*/
public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
if (enable) {
sendMessage(CMD_START_AP, wifiConfig);
} else {
sendMessage(CMD_STOP_AP);
}
}
public void setWifiApConfiguration(WifiConfiguration config) {
mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
}
public WifiConfiguration syncGetWifiApConfiguration() {
Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
resultMsg.recycle();
return ret;
}
/**
* TODO: doc
*/
public int syncGetWifiState() {
return mWifiState.get();
}
/**
* TODO: doc
*/
public String syncGetWifiStateByName() {
switch (mWifiState.get()) {
case WIFI_STATE_DISABLING:
return "disabling";
case WIFI_STATE_DISABLED:
return "disabled";
case WIFI_STATE_ENABLING:
return "enabling";
case WIFI_STATE_ENABLED:
return "enabled";
case WIFI_STATE_UNKNOWN:
return "unknown state";
default:
return "[invalid state]";
}
}
/**
* TODO: doc
*/
public int syncGetWifiApState() {
return mWifiApState.get();
}
/**
* TODO: doc
*/
public String syncGetWifiApStateByName() {
switch (mWifiApState.get()) {
case WIFI_AP_STATE_DISABLING:
return "disabling";
case WIFI_AP_STATE_DISABLED:
return "disabled";
case WIFI_AP_STATE_ENABLING:
return "enabling";
case WIFI_AP_STATE_ENABLED:
return "enabled";
case WIFI_AP_STATE_FAILED:
return "failed";
default:
return "[invalid state]";
}
}
/**
* Get status information for the current connection, if any.
* @return a {@link WifiInfo} object containing information about the current connection
*
*/
public WifiInfo syncRequestConnectionInfo() {
return mWifiInfo;
}
public DhcpResults syncGetDhcpResults() {
synchronized (mDhcpResultsLock) {
return new DhcpResults(mDhcpResults);
}
}
/**
* TODO: doc
*/
public void setDriverStart(boolean enable) {
if (enable) {
sendMessage(CMD_START_DRIVER);
} else {
sendMessage(CMD_STOP_DRIVER);
}
}
/**
* TODO: doc
*/
public void setOperationalMode(int mode) {
if (DBG) log("setting operational mode to " + String.valueOf(mode));
sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
}
/**
* TODO: doc
*/
public List<ScanResult> syncGetScanResultsList() {
synchronized (mScanResultCache) {
List<ScanResult> scanList = new ArrayList<ScanResult>();
for(ScanResult result: mScanResults) {
scanList.add(new ScanResult(result));
}
return scanList;
}
}
/**
* Get unsynchronized pointer to scan result list
* Can be called only from AutoJoinController which runs in the WifiStateMachine context
*/
public List<ScanResult> getScanResultsListNoCopyUnsync() {
return mScanResults;
}
/**
* Disconnect from Access Point
*/
public void disconnectCommand() {
sendMessage(CMD_DISCONNECT);
}
/**
* Initiate a reconnection to AP
*/
public void reconnectCommand() {
sendMessage(CMD_RECONNECT);
}
/**
* Initiate a re-association to AP
*/
public void reassociateCommand() {
sendMessage(CMD_REASSOCIATE);
}
/**
* Reload networks and then reconnect; helps load correct data for TLS networks
*/
public void reloadTlsNetworksAndReconnect() {
sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
}
/**
* Add a network synchronously
*
* @return network id of the new network
*/
public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
int result = resultMsg.arg1;
resultMsg.recycle();
return result;
}
/**
* Get configured networks synchronously
* @param channel
* @return
*/
public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid);
List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
resultMsg.recycle();
return result;
}
public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(
CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS);
List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
resultMsg.recycle();
return result;
}
/**
* Get connection statistics synchronously
* @param channel
* @return
*/
public WifiConnectionStatistics syncGetConnectionStatistics(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONNECTION_STATISTICS);
WifiConnectionStatistics result = (WifiConnectionStatistics) resultMsg.obj;
resultMsg.recycle();
return result;
}
/**
* Get adaptors synchronously
*/
public int syncGetSupportedFeatures(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_SUPPORTED_FEATURES);
int supportedFeatureSet = resultMsg.arg1;
resultMsg.recycle();
return supportedFeatureSet;
}
/**
* Get link layers stats for adapter synchronously
*/
public WifiLinkLayerStats syncGetLinkLayerStats(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_LINK_LAYER_STATS);
WifiLinkLayerStats result = (WifiLinkLayerStats) resultMsg.obj;
resultMsg.recycle();
return result;
}
/**
* Delete a network
*
* @param networkId id of the network to be removed
*/
public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
boolean result = (resultMsg.arg1 != FAILURE);
resultMsg.recycle();
return result;
}
/**
* Enable a network
*
* @param netId network id of the network
* @param disableOthers true, if all other networks have to be disabled
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
disableOthers ? 1 : 0);
boolean result = (resultMsg.arg1 != FAILURE);
resultMsg.recycle();
return result;
}
/**
* Disable a network
*
* @param netId network id of the network
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
resultMsg.recycle();
return result;
}
/**
* Retrieves a WPS-NFC configuration token for the specified network
* @return a hex string representation of the WPS-NFC configuration token
*/
public String syncGetWpsNfcConfigurationToken(int netId) {
return mWifiNative.getNfcWpsConfigurationToken(netId);
}
/**
* 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(CMD_BLACKLIST_NETWORK, bssid);
}
/**
* Clear the blacklist list
*
*/
public void clearBlacklist() {
sendMessage(CMD_CLEAR_BLACKLIST);
}
public void enableRssiPolling(boolean enabled) {
sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
}
public void enableAllNetworks() {
sendMessage(CMD_ENABLE_ALL_NETWORKS);
}
/**
* Start filtering Multicast v4 packets
*/
public void startFilteringMulticastV4Packets() {
mFilteringMulticastV4Packets.set(true);
sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
}
/**
* Stop filtering Multicast v4 packets
*/
public void stopFilteringMulticastV4Packets() {
mFilteringMulticastV4Packets.set(false);
sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
}
/**
* Start filtering Multicast v4 packets
*/
public void startFilteringMulticastV6Packets() {
sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
}
/**
* Stop filtering Multicast v4 packets
*/
public void stopFilteringMulticastV6Packets() {
sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
}
/**
* Set high performance mode of operation.
* Enabling would set active power mode and disable suspend optimizations;
* disabling would set auto power mode and enable suspend optimizations
* @param enable true if enable, false otherwise
*/
public void setHighPerfModeEnabled(boolean enable) {
sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
}
/**
* Set the country code
* @param countryCode following ISO 3166 format
* @param persist {@code true} if the setting should be remembered.
*/
public void setCountryCode(String countryCode, boolean persist) {
// If it's a good country code, apply after the current
// wifi connection is terminated; ignore resetting of code
// for now (it is unclear what the chipset should do when
// country code is reset)
int countryCodeSequence = mCountryCodeSequence.incrementAndGet();
if (TextUtils.isEmpty(countryCode)) {
log("Ignoring resetting of country code");
} else {
sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode);
}
}
/**
* Set the operational frequency band
* @param band
* @param persist {@code true} if the setting should be remembered.
*/
public void setFrequencyBand(int band, boolean persist) {
if (persist) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.WIFI_FREQUENCY_BAND,
band);
}
sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
}
/**
* Enable TDLS for a specific MAC address
*/
public void enableTdls(String remoteMacAddress, boolean enable) {
int enabler = enable ? 1 : 0;
sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
}
/**
* Returns the operational frequency band
*/
public int getFrequencyBand() {
return mFrequencyBand.get();
}
/**
* Returns the wifi configuration file
*/
public String getConfigFile() {
return mWifiConfigStore.getConfigFile();
}
/**
* Send a message indicating bluetooth adapter connection state changed
*/
public void sendBluetoothAdapterStateChange(int state) {
sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
}
/**
* Save configuration on supplicant
*
* @return {@code true} if the operation succeeds, {@code false} otherwise
*
* TODO: deprecate this
*/
public boolean syncSaveConfig(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
boolean result = (resultMsg.arg1 != FAILURE);
resultMsg.recycle();
return result;
}
public void updateBatteryWorkSource(WorkSource newSource) {
synchronized (mRunningWifiUids) {
try {
if (newSource != null) {
mRunningWifiUids.set(newSource);
}
if (mIsRunning) {
if (mReportedRunning) {
// If the work source has changed since last time, need
// to remove old work from battery stats.
if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
mRunningWifiUids);
mLastRunningWifiUids.set(mRunningWifiUids);
}
} else {
// Now being started, report it.
mBatteryStats.noteWifiRunning(mRunningWifiUids);
mLastRunningWifiUids.set(mRunningWifiUids);
mReportedRunning = true;
}
} else {
if (mReportedRunning) {
// Last reported we were running, time to stop.
mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
mLastRunningWifiUids.clear();
mReportedRunning = false;
}
}
mWakeLock.setWorkSource(newSource);
} catch (RemoteException ignore) {
}
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
mSupplicantStateTracker.dump(fd, pw, args);
pw.println("mLinkProperties " + mLinkProperties);
pw.println("mWifiInfo " + mWifiInfo);
pw.println("mDhcpResults " + mDhcpResults);
pw.println("mNetworkInfo " + mNetworkInfo);
pw.println("mLastSignalLevel " + mLastSignalLevel);
pw.println("mLastBssid " + mLastBssid);
pw.println("mLastNetworkId " + mLastNetworkId);
pw.println("mOperationalMode " + mOperationalMode);
pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
pw.println("Supplicant status " + mWifiNative.status(true));
pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
pw.println("mLastSetCountryCode " + mLastSetCountryCode);
pw.println("mPersistedCountryCode " + mPersistedCountryCode);
pw.println();
mWifiConfigStore.dump(fd, pw, args);
}
/*********************************************************
* Internal private functions
********************************************************/
private void logStateAndMessage(Message message, String state) {
messageHandlingStatus = 0;
if (mLogMessages) {
//long now = SystemClock.elapsedRealtimeNanos();
//String ts = String.format("[%,d us]", now/1000);
loge( " " + state + " " + getLogRecString(message));
}
}
/**
* Return the additional string to be logged by LogRec, default
*
* @param msg that was processed
* @return information to be logged as a String
*/
protected String getLogRecString(Message msg) {
WifiConfiguration config;
Long now;
long milli;
String report;
StringBuilder sb = new StringBuilder();
if (mScreenOn) {
sb.append("!");
}
if (messageHandlingStatus != MESSAGE_HANDLING_STATUS_UNKNOWN) {
sb.append("(").append(messageHandlingStatus).append(")");
}
sb.append(smToString(msg));
if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
sb.append(" uid=" + msg.sendingUid);
}
switch (msg.what) {
case CMD_START_SCAN:
now = System.currentTimeMillis();
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" ic=");
sb.append(Integer.toString(sScanAlarmIntentCount));
if (msg.obj != null) {
Bundle bundle = (Bundle)msg.obj;
Long request = bundle.getLong(SCAN_REQUEST_TIME, 0);
if (request != 0) {
sb.append(" proc(ms):").append(now - request);
}
}
if (mIsScanOngoing) sb.append(" onGoing");
if (mIsFullScanOngoing) sb.append(" full");
if (lastStartScanTimeStamp != 0) {
sb.append(" started:").append(lastStartScanTimeStamp);
sb.append(",").append(now - lastStartScanTimeStamp);
}
if (lastScanDuration != 0) {
sb.append(" dur:").append(lastScanDuration);
}
sb.append(" rssi=").append(mWifiInfo.getRssi());
sb.append(" f=").append(mWifiInfo.getFrequency());
sb.append(" sc=").append(mWifiInfo.score);
sb.append(" link=").append(mWifiInfo.getLinkSpeed());
sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate));
sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate));
sb.append(String.format(" %.1f ", mWifiInfo.txBadRate));
sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate));
if (lastScanFreqs != null) {
sb.append(" list=").append(lastScanFreqs);
} else {
sb.append(" fiv=").append(fullBandConnectedTimeIntervalMilli);
}
report = reportOnTime();
if (report != null) {
sb.append(" ").append(report);
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
milli = SystemClock.elapsedRealtime();
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" rt=").append(milli).append(" ");
StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
if (stateChangeResult != null) {
sb.append(stateChangeResult.toString());
}
break;
case WifiManager.SAVE_NETWORK:
case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (lastSavedConfigurationAttempt != null) {
sb.append(" ").append(lastSavedConfigurationAttempt.configKey());
sb.append(" nid=").append(lastSavedConfigurationAttempt.networkId);
if (lastSavedConfigurationAttempt.hiddenSSID) {
sb.append(" hidden");
}
if (lastSavedConfigurationAttempt.preSharedKey != null
&& !lastSavedConfigurationAttempt.preSharedKey.equals("*")) {
sb.append(" hasPSK");
}
if (lastSavedConfigurationAttempt.ephemeral) {
sb.append(" ephemeral");
}
if (lastSavedConfigurationAttempt.selfAdded) {
sb.append(" selfAdded");
}
sb.append(" cuid=").append(lastSavedConfigurationAttempt.creatorUid);
sb.append(" suid=").append(lastSavedConfigurationAttempt.lastUpdateUid);
}
break;
case WifiManager.FORGET_NETWORK:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (lastForgetConfigurationAttempt != null) {
sb.append(" ").append(lastForgetConfigurationAttempt.configKey());
sb.append(" nid=").append(lastForgetConfigurationAttempt.networkId);
if (lastForgetConfigurationAttempt.hiddenSSID) {
sb.append(" hidden");
}
if (lastForgetConfigurationAttempt.preSharedKey != null) {
sb.append(" hasPSK");
}
if (lastForgetConfigurationAttempt.ephemeral) {
sb.append(" ephemeral");
}
if (lastForgetConfigurationAttempt.selfAdded) {
sb.append(" selfAdded");
}
sb.append(" cuid=").append(lastForgetConfigurationAttempt.creatorUid);
sb.append(" suid=").append(lastForgetConfigurationAttempt.lastUpdateUid);
sb.append(" ajst=").append(lastForgetConfigurationAttempt.autoJoinStatus);
}
break;
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
String bssid = (String)msg.obj;
if (bssid != null && bssid.length()>0) {
sb.append(" ");
sb.append(bssid);
}
sb.append(" blacklist=" + Boolean.toString(didBlackListBSSID));
milli = SystemClock.elapsedRealtime();
sb.append(" rt=").append(milli);
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (mScanResults != null) {
sb.append(" found=");
sb.append(mScanResults.size());
}
sb.append(" known=").append(mNumScanResultsKnown);
sb.append(" got=").append(mNumScanResultsReturned);
if (lastScanDuration != 0) {
sb.append(" dur:").append(lastScanDuration);
}
if (mOnTime != 0) {
sb.append(" on:").append(mOnTimeThisScan).append(",").append(mOnTimeScan);
sb.append(",").append(mOnTime);
}
if (mTxTime != 0) {
sb.append(" tx:").append(mTxTimeThisScan).append(",").append(mTxTimeScan);
sb.append(",").append(mTxTime);
}
if (mRxTime != 0) {
sb.append(" rx:").append(mRxTimeThisScan).append(",").append(mRxTimeScan);
sb.append(",").append(mRxTime);
}
sb.append(String.format(" bcn=%d", mRunningBeaconCount));
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" ").append(mLastBssid);
sb.append(" nid=").append(mLastNetworkId);
config = getCurrentWifiConfiguration();
if (config != null) {
sb.append(" ").append(config.configKey());
}
milli = SystemClock.elapsedRealtime();
sb.append(" rt=").append(milli);
break;
case CMD_TARGET_BSSID:
case CMD_ASSOCIATED_BSSID:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
sb.append(" BSSID=").append((String)msg.obj);
}
if (mTargetRoamBSSID != null) {
sb.append(" Target=").append(mTargetRoamBSSID);
}
sb.append(" roam=").append(Integer.toString(mAutoRoaming));
milli = SystemClock.elapsedRealtime();
sb.append(" rt=").append(milli);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (msg.obj != null) {
sb.append(" ").append((String)msg.obj);
}
sb.append(" nid=").append(msg.arg1);
sb.append(" reason=").append(msg.arg2);
if (mLastBssid != null) {
sb.append(" lastbssid=").append(mLastBssid);
}
if (mWifiInfo.getFrequency() != -1) {
sb.append(" freq=").append(mWifiInfo.getFrequency());
sb.append(" rssi=").append(mWifiInfo.getRssi());
}
if (linkDebouncing) {
sb.append(" debounce");
}
milli = SystemClock.elapsedRealtime();
sb.append(" rt=").append(milli);
break;
case WifiMonitor.SSID_TEMP_DISABLED:
case WifiMonitor.SSID_REENABLED:
sb.append(" nid=").append(msg.arg1);
if (msg.obj != null) {
sb.append(" ").append((String)msg.obj);
}