blob: ae5182b42a2f2533a463f5b97c11cc92a24d4e53 [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.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT;
import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
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.IpConfiguration;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.MatchAllNetworkSpecifier;
import android.net.NattKeepalivePacketData;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
import android.net.RouteInfo;
import android.net.SocketKeepalive;
import android.net.SocketKeepalive.InvalidPacketException;
import android.net.StaticIpConfiguration;
import android.net.TcpKeepalivePacketData;
import android.net.ip.IIpClient;
import android.net.ip.IpClientCallbacks;
import android.net.ip.IpClientManager;
import android.net.shared.Inet4AddressUtils;
import android.net.shared.ProvisioningConfiguration;
import android.net.wifi.IActionListener;
import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.DeviceMobilityState;
import android.net.wifi.WifiNetworkAgentSpecifier;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStatsManager;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.IBinder;
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.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.system.OsConstants;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.wifi.hotspot2.AnqpEvent;
import com.android.server.wifi.hotspot2.IconEvent;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.WnmData;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
import com.android.server.wifi.proto.nano.WifiMetricsProto;
import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats;
import com.android.server.wifi.util.ExternalCallbackTracker;
import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.RssiUtil;
import com.android.server.wifi.util.TelephonyUtil;
import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
import com.android.server.wifi.util.WifiPermissionsUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
import com.android.wifi.resources.R;
import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implementation of ClientMode. Event handling for Client mode logic is done here,
* and all changes in connectivity state are initiated here.
*
* @hide
*/
public class ClientModeImpl extends StateMachine {
private static final String NETWORKTYPE = "WIFI";
@VisibleForTesting public static final short NUM_LOG_RECS_NORMAL = 100;
@VisibleForTesting public static final short NUM_LOG_RECS_VERBOSE_LOW_MEMORY = 200;
@VisibleForTesting public static final short NUM_LOG_RECS_VERBOSE = 3000;
// Association rejection reason codes
@VisibleForTesting
protected static final int REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
private static final String TAG = "WifiClientModeImpl";
private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
private static final String GOOGLE_OUI = "DA-A1-19";
private static final String EXTRA_OSU_ICON_QUERY_BSSID = "BSSID";
private static final String EXTRA_OSU_ICON_QUERY_FILENAME = "FILENAME";
private static final String EXTRA_OSU_PROVIDER = "OsuProvider";
private static final String EXTRA_UID = "uid";
private static final String EXTRA_PACKAGE_NAME = "PackageName";
private static final String EXTRA_PASSPOINT_CONFIGURATION = "PasspointConfiguration";
private static final int IPCLIENT_TIMEOUT_MS = 60_000;
private boolean mVerboseLoggingEnabled = false;
private final WifiPermissionsWrapper mWifiPermissionsWrapper;
/* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
* the corresponding BSSID.
*/
private boolean mDidBlackListBSSID = false;
/**
* Log with error attribute
*
* @param s is string log
*/
@Override
protected void loge(String s) {
Log.e(getName(), s);
}
@Override
protected void logd(String s) {
Log.d(getName(), s);
}
@Override
protected void log(String s) {
Log.d(getName(), s);
}
private final WifiMetrics mWifiMetrics;
private final WifiInjector mWifiInjector;
private final WifiMonitor mWifiMonitor;
private final WifiNative mWifiNative;
private final WifiPermissionsUtil mWifiPermissionsUtil;
private final WifiConfigManager mWifiConfigManager;
private final WifiConnectivityManager mWifiConnectivityManager;
private final BssidBlocklistMonitor mBssidBlocklistMonitor;
private ConnectivityManager mCm;
private BaseWifiDiagnostics mWifiDiagnostics;
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
private boolean mTemporarilyDisconnectWifi = false;
private final Clock mClock;
private final PropertyService mPropertyService;
private final BuildProperties mBuildProperties;
private final WifiCountryCode mCountryCode;
private final WifiScoreCard mWifiScoreCard;
private final WifiScoreReport mWifiScoreReport;
private final SarManager mSarManager;
private final WifiTrafficPoller mWifiTrafficPoller;
public WifiScoreReport getWifiScoreReport() {
return mWifiScoreReport;
}
private final PasspointManager mPasspointManager;
private final WifiDataStall mWifiDataStall;
private final LinkProbeManager mLinkProbeManager;
private final MboOceController mMboOceController;
private final McastLockManagerFilterController mMcastLockManagerFilterController;
private final ActivityManager mActivityManager;
private boolean mScreenOn = false;
private String mInterfaceName;
private int mLastSignalLevel = -1;
private String mLastBssid;
private int mLastNetworkId; // The network Id we successfully joined
// The subId used by WifiConfiguration with SIM credential which was connected successfully
private int mLastSubId;
private boolean mIpReachabilityDisconnectEnabled = true;
private void processRssiThreshold(byte curRssi, int reason,
WifiNative.WifiRssiEventHandler rssiHandler) {
if (curRssi == Byte.MAX_VALUE || curRssi == Byte.MIN_VALUE) {
Log.wtf(TAG, "processRssiThreshold: Invalid rssi " + curRssi);
return;
}
for (int i = 0; i < mRssiRanges.length; i++) {
if (curRssi < mRssiRanges[i]) {
// Assume sorted values(ascending order) for rssi,
// bounded by high(127) and low(-128) at extremeties
byte maxRssi = mRssiRanges[i];
byte minRssi = mRssiRanges[i - 1];
// This value of hw has to be believed as this value is averaged and has breached
// the rssi thresholds and raised event to host. This would be eggregious if this
// value is invalid
mWifiInfo.setRssi(curRssi);
updateCapabilities();
int ret = startRssiMonitoringOffload(maxRssi, minRssi, rssiHandler);
Log.d(TAG, "Re-program RSSI thresholds for " + getWhatToString(reason)
+ ": [" + minRssi + ", " + maxRssi + "], curRssi=" + curRssi
+ " ret=" + ret);
break;
}
}
}
private boolean mEnableRssiPolling = false;
// Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and the main Wifi thread.
private volatile int mPollRssiIntervalMsecs = DEFAULT_POLL_RSSI_INTERVAL_MSECS;
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 = DISABLED_MODE;
// variable indicating we are expecting a mode switch - do not attempt recovery for failures
private boolean mModeChange = false;
private ClientModeManager.Listener mClientModeCallback = null;
private boolean mBluetoothConnectionActive = false;
private PowerManager.WakeLock mSuspendWakeLock;
/**
* Interval in milliseconds between polling for RSSI and linkspeed information.
* This is also used as the polling interval for WifiTrafficPoller, which updates
* its data activity on every CMD_RSSI_POLL.
*/
private static final int DEFAULT_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 = 4000;
/**
* 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;
/**
* Value to set in wpa_supplicant "bssid" field when we don't want to restrict connection to
* a specific AP.
*/
public static final String SUPPLICANT_BSSID_ANY = "any";
/**
* 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;
private Context mContext;
private final Object mDhcpResultsLock = new Object();
private DhcpResults mDhcpResults;
// NOTE: Do not return to clients - see syncRequestConnectionInfo()
private final ExtendedWifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private SupplicantStateTracker mSupplicantStateTracker;
// Indicates that framework is attempting to roam, set true on CMD_START_ROAM, set false when
// wifi connects or fails to connect
private boolean mIsAutoRoaming = false;
// Roaming failure count
private int mRoamFailCount = 0;
// This is the BSSID we are trying to associate to, it can be set to SUPPLICANT_BSSID_ANY
// if we havent selected a BSSID for joining.
private String mTargetRoamBSSID = SUPPLICANT_BSSID_ANY;
// This one is used to track the current target network ID. This is used for error
// handling during connection setup since many error message from supplicant does not report
// SSID Once connected, it will be set to invalid
private int mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
private long mLastDriverRoamAttempt = 0;
private WifiConfiguration mTargetWifiConfiguration = null;
int getPollRssiIntervalMsecs() {
return mPollRssiIntervalMsecs;
}
void setPollRssiIntervalMsecs(int newPollIntervalMsecs) {
mPollRssiIntervalMsecs = newPollIntervalMsecs;
}
/**
* Method to clear {@link #mTargetRoamBSSID} and reset the the current connected network's
* bssid in wpa_supplicant after a roam/connect attempt.
*/
public boolean clearTargetBssid(String dbg) {
WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (config == null) {
return false;
}
String bssid = SUPPLICANT_BSSID_ANY;
if (config.BSSID != null) {
bssid = config.BSSID;
if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
if (mVerboseLoggingEnabled) {
logd(dbg + " clearTargetBssid " + bssid + " key=" + config.getKey());
}
mTargetRoamBSSID = bssid;
return mWifiNative.setConfiguredNetworkBSSID(mInterfaceName, bssid);
}
/**
* Set Config's default BSSID (for association purpose) and {@link #mTargetRoamBSSID}
* @param config config need set BSSID
* @param bssid default BSSID to assocaite with when connect to this network
* @return false -- does not change the current default BSSID of the configure
* true -- change the current default BSSID of the configur
*/
private boolean setTargetBssid(WifiConfiguration config, String bssid) {
if (config == null || bssid == null) {
return false;
}
if (config.BSSID != null) {
bssid = config.BSSID;
if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
if (mVerboseLoggingEnabled) {
Log.d(TAG, "setTargetBssid set to " + bssid + " key=" + config.getKey());
}
mTargetRoamBSSID = bssid;
config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid);
return true;
}
private volatile IpClientManager mIpClient;
private IpClientCallbacksImpl mIpClientCallbacks;
// Channel for sending replies.
private AsyncChannel mReplyChannel = new AsyncChannel();
// Used to initiate a connection with WifiP2pService
private AsyncChannel mWifiP2pChannel;
private WifiNetworkFactory mNetworkFactory;
private UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
@GuardedBy("mNetworkAgentLock")
private WifiNetworkAgent mNetworkAgent;
private final Object mNetworkAgentLock = new Object();
private byte[] mRssiRanges;
// Used to filter out requests we couldn't possibly satisfy.
private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
// Provide packet filter capabilities to ConnectivityService.
private final NetworkMisc mNetworkMisc = new NetworkMisc();
private final ExternalCallbackTracker<IActionListener> mProcessingActionListeners;
private final ExternalCallbackTracker<ITxPacketCountListener> mProcessingTxPacketCountListeners;
/* The base for wifi message types */
static final int BASE = Protocol.BASE_WIFI;
static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
/* Get adaptors */
static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61;
/* Get Link Layer Stats thru HAL */
static final int CMD_GET_LINK_LAYER_STATS = BASE + 63;
/* Supplicant commands after driver start*/
/* 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;
/* 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;
/* Enables RSSI poll */
static final int CMD_ENABLE_RSSI_POLL = BASE + 82;
/* RSSI poll */
static final int CMD_RSSI_POLL = BASE + 83;
/** Runs RSSI poll once */
static final int CMD_ONESHOT_RSSI_POLL = BASE + 84;
/* Enable suspend mode optimizations in the driver */
static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86;
/* Enable TDLS on a specific MAC address */
static final int CMD_ENABLE_TDLS = BASE + 92;
/**
* Watchdog for protecting against b/16823537
* Leave time for 4-way handshake to succeed
*/
static final int ROAM_GUARD_TIMER_MSEC = 15000;
int mRoamWatchdogCount = 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;
/* Disconnecting state watchdog */
static final int CMD_DISCONNECTING_WATCHDOG_TIMER = BASE + 96;
/* SIM is removed; reset any cached data for it */
static final int CMD_RESET_SIM_NETWORKS = BASE + 101;
/* OSU APIs */
static final int CMD_QUERY_OSU_ICON = BASE + 104;
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
int mDisconnectingWatchdogCount = 0;
static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
/**
* Indicates the end of boot process, should be used to trigger load from config store,
* initiate connection attempt, etc.
* */
static final int CMD_BOOT_COMPLETED = BASE + 134;
/**
* Initialize ClientModeImpl. This is currently used to initialize the
* {@link HalDeviceManager} module.
*/
static final int CMD_INITIALIZE = BASE + 135;
/* 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;
static final int CMD_START_CONNECT = BASE + 143;
private static final int NETWORK_STATUS_UNWANTED_DISCONNECT = 0;
private static final int NETWORK_STATUS_UNWANTED_VALIDATION_FAILED = 1;
private static final int NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN = 2;
static final int CMD_UNWANTED_NETWORK = BASE + 144;
static final int CMD_START_ROAM = BASE + 145;
static final int CMD_NETWORK_STATUS = BASE + 148;
/* A layer 3 neighbor on the Wi-Fi link became unreachable. */
static final int CMD_IP_REACHABILITY_LOST = BASE + 149;
static final int CMD_ACCEPT_UNVALIDATED = BASE + 153;
/* used to offload sending IP packet */
static final int CMD_START_IP_PACKET_OFFLOAD = BASE + 160;
/* used to stop offload sending IP packet */
static final int CMD_STOP_IP_PACKET_OFFLOAD = BASE + 161;
/* used to start rssi monitoring in hw */
static final int CMD_START_RSSI_MONITORING_OFFLOAD = BASE + 162;
/* used to stop rssi moniroting in hw */
static final int CMD_STOP_RSSI_MONITORING_OFFLOAD = BASE + 163;
/* used to indicated RSSI threshold breach in hw */
static final int CMD_RSSI_THRESHOLD_BREACHED = BASE + 164;
/* Enable/Disable WifiConnectivityManager */
static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER = BASE + 166;
/**
* Used to handle messages bounced between ClientModeImpl and IpClient.
*/
static final int CMD_IPV4_PROVISIONING_SUCCESS = BASE + 200;
static final int CMD_IPV4_PROVISIONING_FAILURE = BASE + 201;
/* Push a new APF program to the HAL */
static final int CMD_INSTALL_PACKET_FILTER = BASE + 202;
/* Enable/disable fallback packet filtering */
static final int CMD_SET_FALLBACK_PACKET_FILTERING = BASE + 203;
/* Enable/disable Neighbor Discovery offload functionality. */
static final int CMD_CONFIG_ND_OFFLOAD = BASE + 204;
/* Read the APF program & data buffer */
static final int CMD_READ_PACKET_FILTER = BASE + 208;
/** Used to add packet filter to apf. */
static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = BASE + 209;
/** Used to remove packet filter from apf. */
static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = BASE + 210;
/* Indicates that diagnostics should time out a connection start event. */
static final int CMD_DIAGS_CONNECT_TIMEOUT = BASE + 252;
// Start subscription provisioning with a given provider
private static final int CMD_START_SUBSCRIPTION_PROVISIONING = BASE + 254;
@VisibleForTesting
static final int CMD_PRE_DHCP_ACTION = BASE + 255;
private static final int CMD_PRE_DHCP_ACTION_COMPLETE = BASE + 256;
private static final int CMD_POST_DHCP_ACTION = BASE + 257;
private static final int CMD_CONNECT_NETWORK = BASE + 258;
private static final int CMD_SAVE_NETWORK = BASE + 259;
private static final int CMD_PKT_CNT_FETCH = BASE + 261;
// For message logging.
private static final Class[] sMessageClasses = {
AsyncChannel.class, ClientModeImpl.class };
private static final SparseArray<String> sGetWhatToString =
MessageUtils.findMessageNames(sMessageClasses);
/* 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;
/* DISABLED_MODE - Don't connect, don't scan, don't be an AP */
public static final int DISABLED_MODE = 4;
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;
/**
* Time window in milliseconds for which we send
* {@link NetworkAgent#explicitlySelected(boolean, boolean)}
* after connecting to the network which the user last selected.
*/
@VisibleForTesting
public static final int LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS = 30 * 1000;
/* Tracks if user has enabled suspend optimizations through settings */
private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
/* Tracks if user has enabled Connected Mac Randomization through settings */
/**
* 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;
int mRunningBeaconCount = 0;
/* Default parent state */
private State mDefaultState = new DefaultState();
/* 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();
/* 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();
/**
* 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);
/**
* Work source to use to blame usage on the WiFi service
*/
public static final WorkSource WIFI_WORK_SOURCE = new WorkSource(Process.WIFI_UID);
private final BatteryStatsManager mBatteryStatsManager;
private final TelephonyUtil mTelephonyUtil;
// Used for debug and stats gathering
private static int sScanAlarmIntentCount = 0;
private FrameworkFacade mFacade;
private WifiStateTracker mWifiStateTracker;
private final BackupManagerProxy mBackupManagerProxy;
private final WrongPasswordNotifier mWrongPasswordNotifier;
private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
// Maximum duration to continue to log Wifi usability stats after a data stall is triggered.
@VisibleForTesting
public static final long DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS = 30 * 1000;
private long mDataStallTriggerTimeMs = -1;
private int mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN;
public ClientModeImpl(Context context, FrameworkFacade facade, Looper looper,
UserManager userManager, WifiInjector wifiInjector,
BackupManagerProxy backupManagerProxy, WifiCountryCode countryCode,
WifiNative wifiNative, WrongPasswordNotifier wrongPasswordNotifier,
SarManager sarManager, WifiTrafficPoller wifiTrafficPoller,
LinkProbeManager linkProbeManager,
BatteryStatsManager batteryStatsManager,
SupplicantStateTracker supplicantStateTracker,
MboOceController mboOceController,
TelephonyUtil telephonyUtil) {
super(TAG, looper);
mWifiInjector = wifiInjector;
mWifiMetrics = mWifiInjector.getWifiMetrics();
mClock = wifiInjector.getClock();
mPropertyService = wifiInjector.getPropertyService();
mBuildProperties = wifiInjector.getBuildProperties();
mWifiScoreCard = wifiInjector.getWifiScoreCard();
mContext = context;
mFacade = facade;
mWifiNative = wifiNative;
mBackupManagerProxy = backupManagerProxy;
mWrongPasswordNotifier = wrongPasswordNotifier;
mSarManager = sarManager;
mWifiTrafficPoller = wifiTrafficPoller;
mLinkProbeManager = linkProbeManager;
mMboOceController = mboOceController;
mTelephonyUtil = telephonyUtil;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStatsManager = batteryStatsManager;
mWifiStateTracker = wifiInjector.getWifiStateTracker();
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT);
mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
mWifiConfigManager = mWifiInjector.getWifiConfigManager();
mPasspointManager = mWifiInjector.getPasspointManager();
mWifiMonitor = mWifiInjector.getWifiMonitor();
mWifiDiagnostics = mWifiInjector.getWifiDiagnostics();
mWifiPermissionsWrapper = mWifiInjector.getWifiPermissionsWrapper();
mWifiDataStall = mWifiInjector.getWifiDataStall();
mWifiInfo = new ExtendedWifiInfo(context);
mSupplicantStateTracker = supplicantStateTracker;
mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this);
mBssidBlocklistMonitor = mWifiInjector.getBssidBlocklistMonitor();
mLinkProperties = new LinkProperties();
mMcastLockManagerFilterController = new McastLockManagerFilterController();
mActivityManager = context.getSystemService(ActivityManager.class);
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mLastSignalLevel = -1;
mCountryCode = countryCode;
mWifiScoreReport = new WifiScoreReport(mWifiInjector.getScoringParams(), mClock);
mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
// TODO - needs to be a bit more dynamic
mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
mNetworkCapabilitiesFilter.setNetworkSpecifier(new MatchAllNetworkSpecifier());
// Make the network factories.
mNetworkFactory = mWifiInjector.makeWifiNetworkFactory(
mNetworkCapabilitiesFilter, mWifiConnectivityManager);
// We can't filter untrusted network in the capabilities filter because a trusted
// network would still satisfy a request that accepts untrusted ones.
// We need a second network factory for untrusted network requests because we need a
// different score filter for these requests.
mUntrustedNetworkFactory = mWifiInjector.makeUntrustedWifiNetworkFactory(
mNetworkCapabilitiesFilter, mWifiConnectivityManager);
mWifiNetworkSuggestionsManager = mWifiInjector.getWifiNetworkSuggestionsManager();
mProcessingActionListeners = new ExternalCallbackTracker<>(getHandler());
mProcessingTxPacketCountListeners = new ExternalCallbackTracker<>(getHandler());
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
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);
}
}
}, filter);
mFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
new ContentObserver(getHandler()) {
@Override
public void onChange(boolean selfChange) {
mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
}
});
mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
mSuspendWakeLock.setReferenceCounted(false);
mWifiConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener());
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mConnectModeState, mDefaultState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mDefaultState);
setLogRecSize(NUM_LOG_RECS_NORMAL);
setLogOnlyTransitions(false);
}
@Override
public void start() {
super.start();
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
// Learn the initial state of whether the screen is on.
// We update this field when we receive broadcasts from the system.
handleScreenStateChanged(powerManager.isInteractive());
}
private void registerForWifiMonitorEvents() {
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.TARGET_BSSID_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATED_BSSID_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ANQP_DONE_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.GAS_QUERY_DONE_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.GAS_QUERY_START_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.HS20_REMEDIATION_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_DISCONNECTION_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.RX_HS20_ANQP_ICON_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_REQUEST_IDENTITY,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_REQUEST_SIM_AUTH,
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_DISCONNECTION_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATED_BSSID_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.TARGET_BSSID_EVENT,
mWifiMetrics.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT,
mWifiInjector.getWifiLastResortWatchdog().getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT,
mSupplicantStateTracker.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
mSupplicantStateTracker.getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
mSupplicantStateTracker.getHandler());
}
private void setMulticastFilter(boolean enabled) {
if (mIpClient != null) {
mIpClient.setMulticastFilter(enabled);
}
}
/**
* Class to implement the MulticastLockManager.FilterController callback.
*/
class McastLockManagerFilterController implements WifiMulticastLockManager.FilterController {
/**
* Start filtering Multicast v4 packets
*/
public void startFilteringMulticastPackets() {
setMulticastFilter(true);
}
/**
* Stop filtering Multicast v4 packets
*/
public void stopFilteringMulticastPackets() {
setMulticastFilter(false);
}
}
class IpClientCallbacksImpl extends IpClientCallbacks {
private final ConditionVariable mWaitForCreationCv = new ConditionVariable(false);
private final ConditionVariable mWaitForStopCv = new ConditionVariable(false);
@Override
public void onIpClientCreated(IIpClient ipClient) {
mIpClient = new IpClientManager(ipClient, getName());
mWaitForCreationCv.open();
}
@Override
public void onPreDhcpAction() {
sendMessage(CMD_PRE_DHCP_ACTION);
}
@Override
public void onPostDhcpAction() {
sendMessage(CMD_POST_DHCP_ACTION);
}
@Override
public void onNewDhcpResults(DhcpResults dhcpResults) {
if (dhcpResults != null) {
sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
} else {
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
}
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST);
sendMessage(CMD_IP_CONFIGURATION_LOST);
}
@Override
public void onLinkPropertiesChange(LinkProperties newLp) {
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
}
@Override
public void onReachabilityLost(String logMsg) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
}
@Override
public void installPacketFilter(byte[] filter) {
sendMessage(CMD_INSTALL_PACKET_FILTER, filter);
}
@Override
public void startReadPacketFilter() {
sendMessage(CMD_READ_PACKET_FILTER);
}
@Override
public void setFallbackMulticastFilter(boolean enabled) {
sendMessage(CMD_SET_FALLBACK_PACKET_FILTERING, enabled);
}
@Override
public void setNeighborDiscoveryOffload(boolean enabled) {
sendMessage(CMD_CONFIG_ND_OFFLOAD, (enabled ? 1 : 0));
}
@Override
public void onQuit() {
mWaitForStopCv.open();
}
boolean awaitCreation() {
return mWaitForCreationCv.block(IPCLIENT_TIMEOUT_MS);
}
boolean awaitShutdown() {
return mWaitForStopCv.block(IPCLIENT_TIMEOUT_MS);
}
}
private void stopIpClient() {
/* Restore power save and suspend optimizations */
handlePostDhcpSetup();
if (mIpClient != null) {
mIpClient.stop();
}
}
/**
* Listener for config manager network config related events.
* TODO (b/117601161) : Move some of the existing handling in WifiConnectivityManager's listener
* for the same events.
*/
private class OnNetworkUpdateListener implements
WifiConfigManager.OnNetworkUpdateListener {
@Override
public void onNetworkAdded(WifiConfiguration config) { }
@Override
public void onNetworkEnabled(WifiConfiguration config) { }
@Override
public void onNetworkRemoved(WifiConfiguration config) {
// The current connected or connecting network has been removed, trigger a disconnect.
if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
// Disconnect and let autojoin reselect a new network
sendMessage(CMD_DISCONNECT);
}
mWifiNative.removeNetworkCachedData(config.networkId);
}
@Override
public void onNetworkUpdated(WifiConfiguration config) {
// User might have changed meteredOverride, so update capabilities
if (config.networkId == mLastNetworkId) {
updateCapabilities();
}
}
@Override
public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) {
if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
// Disconnect and let autojoin reselect a new network
sendMessage(CMD_DISCONNECT);
}
}
@Override
public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) {
// For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network
// because supplicant won't be trying to reconnect. If this is due to a
// preventAutomaticReconnect request from ConnectivityService, that service
// will disconnect as appropriate.
if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
// Disconnect and let autojoin reselect a new network
sendMessage(CMD_DISCONNECT);
}
}
}
/**
* Set wpa_supplicant log level using |mVerboseLoggingLevel| flag.
*/
void setSupplicantLogLevel() {
mWifiNative.setSupplicantLogLevel(mVerboseLoggingEnabled);
}
/**
* Method to update logging level in wifi service related classes.
*
* @param verbose int logging level to use
*/
public void enableVerboseLogging(int verbose) {
if (verbose > 0) {
mVerboseLoggingEnabled = true;
setLogRecSize(mActivityManager.isLowRamDevice()
? NUM_LOG_RECS_VERBOSE_LOW_MEMORY : NUM_LOG_RECS_VERBOSE);
} else {
mVerboseLoggingEnabled = false;
setLogRecSize(NUM_LOG_RECS_NORMAL);
}
configureVerboseHalLogging(mVerboseLoggingEnabled);
setSupplicantLogLevel();
mCountryCode.enableVerboseLogging(verbose);
mWifiScoreReport.enableVerboseLogging(mVerboseLoggingEnabled);
mWifiDiagnostics.enableVerboseLogging(mVerboseLoggingEnabled);
mWifiMonitor.enableVerboseLogging(verbose);
mWifiNative.enableVerboseLogging(verbose);
mWifiConfigManager.enableVerboseLogging(verbose);
mSupplicantStateTracker.enableVerboseLogging(verbose);
mPasspointManager.enableVerboseLogging(verbose);
mNetworkFactory.enableVerboseLogging(verbose);
mLinkProbeManager.enableVerboseLogging(mVerboseLoggingEnabled);
mMboOceController.enableVerboseLogging(mVerboseLoggingEnabled);
}
private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL";
private static final String LOGD_LEVEL_DEBUG = "D";
private static final String LOGD_LEVEL_VERBOSE = "V";
private void configureVerboseHalLogging(boolean enableVerbose) {
if (mBuildProperties.isUserBuild()) { // Verbose HAL logging not supported on user builds.
return;
}
mPropertyService.set(SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL,
enableVerbose ? LOGD_LEVEL_VERBOSE : LOGD_LEVEL_DEBUG);
}
private boolean setRandomMacOui() {
String oui = mContext.getResources().getString(R.string.config_wifi_random_mac_oui);
if (TextUtils.isEmpty(oui)) {
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(mInterfaceName, ouiBytes);
}
/**
* Initiates connection to a network specified by the user/app. This method checks if the
* requesting app holds the NETWORK_SETTINGS permission.
*
* @param netId Id network to initiate connection.
* @param uid UID of the app requesting the connection.
* @param forceReconnect Whether to force a connection even if we're connected to the same
* network currently.
*/
private void connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
+ ", forceReconnect = " + forceReconnect);
if (!forceReconnect && mWifiInfo.getNetworkId() == netId) {
// We're already connected to the user specified network, don't trigger a
// reconnection unless it was forced.
logi("connectToUserSelectNetwork already connecting/connected=" + netId);
} else {
mWifiConnectivityManager.prepareForForcedConnection(netId);
if (uid == Process.SYSTEM_UID) {
mWifiMetrics.setNominatorForNetwork(netId,
WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
}
startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
}
}
/**
* ******************************************************
* Methods exposed for public use
* ******************************************************
*/
/**
* Retrieve a Messenger for the ClientModeImpl Handler
*
* @return Messenger
*/
public Messenger getMessenger() {
return new Messenger(getHandler());
}
// Last connect attempt is used to prevent scan requests:
// - for a period of 10 seconds after attempting to connect
private long mLastConnectAttemptTimestamp = 0;
// For debugging, keep track of last message status handling
// TODO, find an equivalent mechanism as part of parent class
private static final int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
private static final int MESSAGE_HANDLING_STATUS_OK = 1;
private static final int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
private static final int MESSAGE_HANDLING_STATUS_REFUSED = -1;
private static final int MESSAGE_HANDLING_STATUS_FAIL = -2;
private static final int MESSAGE_HANDLING_STATUS_OBSOLETE = -3;
private static final int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
private static final int MESSAGE_HANDLING_STATUS_DISCARD = -5;
private static final int MESSAGE_HANDLING_STATUS_LOOPED = -6;
private static final int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
private int mMessageHandlingStatus = 0;
private int mOnTime = 0;
private int mTxTime = 0;
private int mRxTime = 0;
private int mOnTimeScreenStateChange = 0;
private long mLastOntimeReportTimeStamp = 0;
private long mLastScreenStateChangeTimeStamp = 0;
private int mOnTimeLastReport = 0;
private int mTxTimeLastReport = 0;
private int mRxTimeLastReport = 0;
private WifiLinkLayerStats mLastLinkLayerStats;
private long mLastLinkLayerStatsUpdate = 0;
String reportOnTime() {
long now = mClock.getWallClockMillis();
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 - mLastOntimeReportTimeStamp);
mLastOntimeReportTimeStamp = 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 - mLastScreenStateChangeTimeStamp);
sb.append(String.format(" from screen [on:%d period:%d]", on, period));
return sb.toString();
}
WifiLinkLayerStats getWifiLinkLayerStats() {
if (mInterfaceName == null) {
loge("getWifiLinkLayerStats called without an interface");
return null;
}
mLastLinkLayerStatsUpdate = mClock.getWallClockMillis();
WifiLinkLayerStats stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName);
if (stats != null) {
mOnTime = stats.on_time;
mTxTime = stats.tx_time;
mRxTime = stats.rx_time;
mRunningBeaconCount = stats.beacon_rx;
mWifiInfo.updatePacketRates(stats, mLastLinkLayerStatsUpdate);
} else {
long mTxPkts = mFacade.getTxPackets(mInterfaceName);
long mRxPkts = mFacade.getRxPackets(mInterfaceName);
mWifiInfo.updatePacketRates(mTxPkts, mRxPkts, mLastLinkLayerStatsUpdate);
}
return stats;
}
private byte[] getDstMacForKeepalive(KeepalivePacketData packetData)
throws InvalidPacketException {
try {
InetAddress gateway = RouteInfo.selectBestRoute(
mLinkProperties.getRoutes(), packetData.dstAddress).getGateway();
String dstMacStr = macAddressFromRoute(gateway.getHostAddress());
return NativeUtil.macAddressToByteArray(dstMacStr);
} catch (NullPointerException | IllegalArgumentException e) {
throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
}
}
private static int getEtherProtoForKeepalive(KeepalivePacketData packetData)
throws InvalidPacketException {
if (packetData.dstAddress instanceof Inet4Address) {
return OsConstants.ETH_P_IP;
} else if (packetData.dstAddress instanceof Inet6Address) {
return OsConstants.ETH_P_IPV6;
} else {
throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
}
}
private int startWifiIPPacketOffload(int slot, KeepalivePacketData packetData,
int intervalSeconds) {
byte[] packet = null;
byte[] dstMac = null;
int proto = 0;
try {
packet = packetData.getPacket();
dstMac = getDstMacForKeepalive(packetData);
proto = getEtherProtoForKeepalive(packetData);
} catch (InvalidPacketException e) {
return e.error;
}
int ret = mWifiNative.startSendingOffloadedPacket(
mInterfaceName, slot, dstMac, packet, proto, intervalSeconds * 1000);
if (ret != 0) {
loge("startWifiIPPacketOffload(" + slot + ", " + intervalSeconds
+ "): hardware error " + ret);
return SocketKeepalive.ERROR_HARDWARE_ERROR;
} else {
return SocketKeepalive.SUCCESS;
}
}
private int stopWifiIPPacketOffload(int slot) {
int ret = mWifiNative.stopSendingOffloadedPacket(mInterfaceName, slot);
if (ret != 0) {
loge("stopWifiIPPacketOffload(" + slot + "): hardware error " + ret);
return SocketKeepalive.ERROR_HARDWARE_ERROR;
} else {
return SocketKeepalive.SUCCESS;
}
}
private int startRssiMonitoringOffload(byte maxRssi, byte minRssi,
WifiNative.WifiRssiEventHandler rssiHandler) {
return mWifiNative.startRssiMonitoring(mInterfaceName, maxRssi, minRssi, rssiHandler);
}
private int stopRssiMonitoringOffload() {
return mWifiNative.stopRssiMonitoring(mInterfaceName);
}
/**
* Temporary method that allows the active ClientModeManager to set the wifi state that is
* retrieved by API calls. This will be removed when WifiServiceImpl no longer directly calls
* this class (b/31479117).
*
* @param newState new state to set, invalid states are ignored.
*/
public void setWifiStateForApiCalls(int newState) {
switch (newState) {
case WIFI_STATE_DISABLING:
case WIFI_STATE_DISABLED:
case WIFI_STATE_ENABLING:
case WIFI_STATE_ENABLED:
case WIFI_STATE_UNKNOWN:
if (mVerboseLoggingEnabled) {
Log.d(TAG, "setting wifi state to: " + newState);
}
mWifiState.set(newState);
return;
default:
Log.d(TAG, "attempted to set an invalid state: " + newState);
return;
}
}
/**
* Method used by WifiServiceImpl to get the current state of Wifi (in client mode) for API
* calls. This will be removed when WifiService no longer directly calls this class
* (b/31479117).
*/
public int syncGetWifiState() {
return mWifiState.get();
}
/**
* Converts the current wifi state to a printable form.
*/
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]";
}
}
public boolean isConnected() {
return getCurrentState() == mConnectedState;
}
public boolean isDisconnected() {
return getCurrentState() == mDisconnectedState;
}
/**
* Method checking if supplicant is in a transient state
*
* @return boolean true if in transient state
*/
public boolean isSupplicantTransientState() {
SupplicantState supplicantState = mWifiInfo.getSupplicantState();
if (supplicantState == SupplicantState.ASSOCIATING
|| supplicantState == SupplicantState.AUTHENTICATING
|| supplicantState == SupplicantState.FOUR_WAY_HANDSHAKE
|| supplicantState == SupplicantState.GROUP_HANDSHAKE) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Supplicant is under transient state: " + supplicantState);
}
return true;
} else {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Supplicant is under steady state: " + supplicantState);
}
}
return false;
}
/**
* Get status information for the current connection, if any.
*
* @return a {@link WifiInfo} object containing information about the current connection
*/
public WifiInfo syncRequestConnectionInfo() {
WifiInfo result = new WifiInfo(mWifiInfo);
return result;
}
/**
* Method to retrieve the current WifiInfo
*
* @returns WifiInfo
*/
public WifiInfo getWifiInfo() {
return mWifiInfo;
}
/**
* Blocking call to get the current DHCP results
*
* @return DhcpResults current results
*/
public DhcpResults syncGetDhcpResults() {
synchronized (mDhcpResultsLock) {
return new DhcpResults(mDhcpResults);
}
}
/**
* When the underlying interface is destroyed, we must immediately tell connectivity service to
* mark network agent as disconnected and stop the ip client.
*/
public void handleIfaceDestroyed() {
handleNetworkDisconnect();
}
/**
* TODO: doc
*/
public void setOperationalMode(int mode, String ifaceName) {
if (mVerboseLoggingEnabled) {
log("setting operational mode to " + String.valueOf(mode) + " for iface: " + ifaceName);
}
mModeChange = true;
if (mode != CONNECT_MODE) {
// we are disabling client mode... need to exit connect mode now
transitionTo(mDefaultState);
} else {
// do a quick sanity check on the iface name, make sure it isn't null
if (ifaceName != null) {
mInterfaceName = ifaceName;
transitionTo(mDisconnectedState);
} else {
Log.e(TAG, "supposed to enter connect mode, but iface is null -> DefaultState");
transitionTo(mDefaultState);
}
}
// use the CMD_SET_OPERATIONAL_MODE to force the transitions before other messages are
// handled.
sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
}
/**
* Initiates a system-level bugreport, in a non-blocking fashion.
*/
public void takeBugReport(String bugTitle, String bugDetail) {
mWifiDiagnostics.takeBugReport(bugTitle, bugDetail);
}
/**
* Allow tests to confirm the operational mode for ClientModeImpl for testing.
*/
@VisibleForTesting
protected int getOperationalModeForTest() {
return mOperationalMode;
}
/**
* Retrieve the WifiMulticastLockManager.FilterController callback for registration.
*/
protected WifiMulticastLockManager.FilterController getMcastLockManagerFilterController() {
return mMcastLockManagerFilterController;
}
/**
* Blocking method to retrieve the passpoint icon.
*
* @param channel AsyncChannel for the response
* @param bssid representation of the bssid as a long
* @param fileName name of the file
*
* @return boolean returning the result of the call
*/
public boolean syncQueryPasspointIcon(AsyncChannel channel, long bssid, String fileName) {
Bundle bundle = new Bundle();
bundle.putLong(EXTRA_OSU_ICON_QUERY_BSSID, bssid);
bundle.putString(EXTRA_OSU_ICON_QUERY_FILENAME, fileName);
Message resultMsg = channel.sendMessageSynchronously(CMD_QUERY_OSU_ICON, bundle);
int result = resultMsg.arg1;
resultMsg.recycle();
return result == 1;
}
/**
* Deauthenticate and set the re-authentication hold off time for the current network
* @param holdoff hold off time in milliseconds
* @param ess set if the hold off pertains to an ESS rather than a BSS
*/
public void deauthenticateNetwork(AsyncChannel channel, long holdoff, boolean ess) {
// TODO: This needs an implementation
}
/**
* Disconnect from Access Point
*/
public void disconnectCommand() {
sendMessage(CMD_DISCONNECT);
}
/**
* Method to trigger a disconnect.
*
* @param uid UID of requesting caller
* @param reason disconnect reason
*/
public void disconnectCommand(int uid, int reason) {
sendMessage(CMD_DISCONNECT, uid, reason);
}
/**
* Initiate a reconnection to AP
*/
public void reconnectCommand(WorkSource workSource) {
sendMessage(CMD_RECONNECT, workSource);
}
/**
* Initiate a re-association to AP
*/
public void reassociateCommand() {
sendMessage(CMD_REASSOCIATE);
}
/**
* Checks for a null Message.
*
* This can happen with sendMessageSynchronously, for example if an
* InterruptedException occurs. If this just happens once, silently
* ignore it, because it is probably a side effect of shutting down.
* If it happens a second time, generate a WTF.
*/
private boolean messageIsNull(Message resultMsg) {
if (resultMsg != null) return false;
if (mNullMessageCounter.getAndIncrement() > 0) {
Log.wtf(TAG, "Persistent null Message", new RuntimeException());
}
return true;
}
private AtomicInteger mNullMessageCounter = new AtomicInteger(0);
/**
* Start subscription provisioning synchronously
*
* @param provider {@link OsuProvider} the provider to provision with
* @param callback {@link IProvisioningCallback} callback for provisioning status
* @return boolean true indicates provisioning was started, false otherwise
*/
public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider,
IProvisioningCallback callback, AsyncChannel channel) {
Message msg = Message.obtain();
msg.what = CMD_START_SUBSCRIPTION_PROVISIONING;
msg.arg1 = callingUid;
msg.obj = callback;
msg.getData().putParcelable(EXTRA_OSU_PROVIDER, provider);
Message resultMsg = channel.sendMessageSynchronously(msg);
if (messageIsNull(resultMsg)) return false;
boolean result = resultMsg.arg1 != 0;
resultMsg.recycle();
return result;
}
/**
* Get the supported feature set synchronously
*/
public long syncGetSupportedFeatures(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_SUPPORTED_FEATURES);
if (messageIsNull(resultMsg)) return 0;
long supportedFeatureSet = ((Long) resultMsg.obj).longValue();
resultMsg.recycle();
// Mask the feature set against system properties.
boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT);
if (!rttSupported) {
// flags filled in by vendor HAL, remove if overlay disables it.
supportedFeatureSet &=
~(WifiManager.WIFI_FEATURE_D2D_RTT | WifiManager.WIFI_FEATURE_D2AP_RTT);
}
if (!mContext.getResources().getBoolean(
R.bool.config_wifi_p2p_mac_randomization_supported)) {
// flags filled in by vendor HAL, remove if overlay disables it.
supportedFeatureSet &= ~WifiManager.WIFI_FEATURE_P2P_RAND_MAC;
}
if (mContext.getResources().getBoolean(
R.bool.config_wifi_connected_mac_randomization_supported)) {
// no corresponding flags in vendor HAL, set if overlay enables it.
supportedFeatureSet |= WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC;
}
if (mContext.getResources().getBoolean(
R.bool.config_wifi_ap_mac_randomization_supported)) {
// no corresponding flags in vendor HAL, set if overlay enables it.
supportedFeatureSet |= WifiManager.WIFI_FEATURE_AP_RAND_MAC;
}
return supportedFeatureSet;
}
/**
* Get link layers stats for adapter synchronously
*/
public WifiLinkLayerStats syncGetLinkLayerStats(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_LINK_LAYER_STATS);
if (messageIsNull(resultMsg)) return null;
WifiLinkLayerStats result = (WifiLinkLayerStats) resultMsg.obj;
resultMsg.recycle();
return result;
}
/**
* Method to enable/disable RSSI polling
* @param enabled boolean idicating if polling should start
*/
public void enableRssiPolling(boolean enabled) {
sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 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);
}
/**
* reset cached SIM credential data
*/
public synchronized void resetSimAuthNetworks(boolean simPresent) {
sendMessage(CMD_RESET_SIM_NETWORKS, simPresent ? 1 : 0);
}
/**
* Get Network object of current wifi network
* @return Network object of current wifi network
*/
public Network getCurrentNetwork() {
synchronized (mNetworkAgentLock) {
if (mNetworkAgent != null) {
return new Network(mNetworkAgent.netId);
} else {
return null;
}
}
}
/**
* 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);
}
/**
* Send a message indicating bluetooth adapter connection state changed
*/
public void sendBluetoothAdapterStateChange(int state) {
sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
}
/**
* Trigger dump on the class IpClient object.
*/
public void dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mIpClient != null) {
// All dumpIpClient does is print this log message.
// TODO: consider deleting this, since it's not useful.
pw.println("IpClient logs have moved to dumpsys network_stack");
}
}
@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("mLastSubId " + mLastSubId);
pw.println("mOperationalMode " + mOperationalMode);
pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
mCountryCode.dump(fd, pw, args);
mNetworkFactory.dump(fd, pw, args);
mUntrustedNetworkFactory.dump(fd, pw, args);
pw.println("Wlan Wake Reasons:" + mWifiNative.getWlanWakeReasonCount());
pw.println();
mWifiConfigManager.dump(fd, pw, args);
pw.println();
mPasspointManager.dump(pw);
pw.println();
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_USER_ACTION);
mWifiDiagnostics.dump(fd, pw, args);
dumpIpClient(fd, pw, args);
mWifiConnectivityManager.dump(fd, pw, args);
mWifiInjector.getWakeupController().dump(fd, pw, args);
mLinkProbeManager.dump(fd, pw, args);
mWifiInjector.getWifiLastResortWatchdog().dump(fd, pw, args);
}
/**
* Trigger message to handle boot completed event.
*/
public void handleBootCompleted() {
sendMessage(CMD_BOOT_COMPLETED);
}
/**
* ******************************************************
* Internal private functions
* ******************************************************
*/
private void logStateAndMessage(Message message, State state) {
mMessageHandlingStatus = 0;
if (mVerboseLoggingEnabled) {
logd(" " + state.getClass().getSimpleName() + " " + getLogRecString(message));
}
}
@Override
protected boolean recordLogRec(Message msg) {
switch (msg.what) {
case CMD_RSSI_POLL:
return mVerboseLoggingEnabled;
default:
return true;
}
}
/**
* Return the additional string to be logged by LogRec, default
*
* @param msg that was processed
* @return information to be logged as a String
*/
@Override
protected String getLogRecString(Message msg) {
WifiConfiguration config;
Long now;
String report;
String key;
StringBuilder sb = new StringBuilder();
sb.append("screen=").append(mScreenOn ? "on" : "off");
if (mMessageHandlingStatus != MESSAGE_HANDLING_STATUS_UNKNOWN) {
sb.append("(").append(mMessageHandlingStatus).append(")");
}
if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
sb.append(" uid=" + msg.sendingUid);
}
switch (msg.what) {
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
if (stateChangeResult != null) {
sb.append(stateChangeResult.toString());
}
break;
case CMD_CONNECT_NETWORK:
case CMD_SAVE_NETWORK: {
NetworkUpdateResult result = (NetworkUpdateResult) msg.obj;
sb.append(" ");
sb.append(Integer.toString(result.netId));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
config = mWifiConfigManager.getConfiguredNetwork(result.netId);
if (config != null) {
sb.append(" ").append(config.getKey());
sb.append(" nid=").append(config.networkId);
if (config.hiddenSSID) {
sb.append(" hidden");
}
if (config.preSharedKey != null
&& !config.preSharedKey.equals("*")) {
sb.append(" hasPSK");
}
if (config.ephemeral) {
sb.append(" ephemeral");
}
sb.append(" cuid=").append(config.creatorUid);
sb.append(" suid=").append(config.lastUpdateUid);
}
break;
}
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
sb.append(" ");
sb.append(" timedOut=" + 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(mDidBlackListBSSID));
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.getKey());
}
key = mWifiConfigManager.getLastSelectedNetworkConfigKey();
if (key != null) {
sb.append(" last=").append(key);
}
break;
case WifiMonitor.TARGET_BSSID_EVENT:
case WifiMonitor.ASSOCIATED_BSSID_EVENT:
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(Boolean.toString(mIsAutoRoaming));
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());
}
break;
case CMD_RSSI_POLL:
case CMD_ONESHOT_RSSI_POLL:
case CMD_UNWANTED_NETWORK:
case CMD_PKT_CNT_FETCH:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (mWifiInfo.getSSID() != null) {
if (mWifiInfo.getSSID() != null) {
sb.append(" ").append(mWifiInfo.getSSID());
}
}
if (mWifiInfo.getBSSID() != null) {
sb.append(" ").append(mWifiInfo.getBSSID());
}
sb.append(" rssi=").append(mWifiInfo.getRssi());
sb.append(" f=").append(mWifiInfo.getFrequency());
sb.append(" sc=").append(mWifiInfo.getScore());
sb.append(" link=").append(mWifiInfo.getLinkSpeed());
sb.append(String.format(" tx=%.1f,", mWifiInfo.getTxSuccessRate()));
sb.append(String.format(" %.1f,", mWifiInfo.getTxRetriesRate()));
sb.append(String.format(" %.1f ", mWifiInfo.getTxBadRate()));
sb.append(String.format(" rx=%.1f", mWifiInfo.getRxSuccessRate()));
sb.append(String.format(" bcn=%d", mRunningBeaconCount));
report = reportOnTime();
if (report != null) {
sb.append(" ").append(report);
}
sb.append(String.format(" score=%d", mWifiInfo.getScore()));
break;
case CMD_START_CONNECT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
config = mWifiConfigManager.getConfiguredNetwork(msg.arg1);
if (config != null) {
sb.append(" ").append(config.getKey());
}
if (mTargetRoamBSSID != null) {
sb.append(" ").append(mTargetRoamBSSID);
}
sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming));
config = getCurrentWifiConfiguration();
if (config != null) {
sb.append(config.getKey());
}
break;
case CMD_START_ROAM:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
ScanResult result = (ScanResult) msg.obj;
if (result != null) {
now = mClock.getWallClockMillis();
sb.append(" bssid=").append(result.BSSID);
sb.append(" rssi=").append(result.level);
sb.append(" freq=").append(result.frequency);
if (result.seen > 0 && result.seen < now) {
sb.append(" seen=").append(now - result.seen);
} else {
// Somehow the timestamp for this scan result is inconsistent
sb.append(" !seen=").append(result.seen);
}
}
if (mTargetRoamBSSID != null) {
sb.append(" ").append(mTargetRoamBSSID);
}
sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming));
sb.append(" fail count=").append(Integer.toString(mRoamFailCount));
break;
case CMD_PRE_DHCP_ACTION:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" txpkts=").append(mWifiInfo.txSuccess);
sb.append(",").append(mWifiInfo.txBad);
sb.append(",").append(mWifiInfo.txRetries);
break;
case CMD_POST_DHCP_ACTION:
if (mLinkProperties != null) {
sb.append(" ");
sb.append(getLinkPropertiesSummary(mLinkProperties));
}
break;
case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
NetworkInfo info = (NetworkInfo) msg.obj;
NetworkInfo.State state = info.getState();
NetworkInfo.DetailedState detailedState = info.getDetailedState();
if (state != null) {
sb.append(" st=").append(state);
}
if (detailedState != null) {
sb.append("/").append(detailedState);
}
}
break;
case CMD_IP_CONFIGURATION_LOST:
int count = -1;
WifiConfiguration c = getCurrentWifiConfiguration();
if (c != null) {
count = c.getNetworkSelectionStatus().getDisableReasonCounter(
WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
}
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" failures: ");
sb.append(Integer.toString(count));
sb.append("/");
sb.append(Integer.toString(mFacade.getIntegerSetting(
mContext, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 0)));
if (mWifiInfo.getBSSID() != null) {
sb.append(" ").append(mWifiInfo.getBSSID());
}
sb.append(String.format(" bcn=%d", mRunningBeaconCount));
break;
case CMD_UPDATE_LINKPROPERTIES:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (mLinkProperties != null) {
sb.append(" ");
sb.append(getLinkPropertiesSummary(mLinkProperties));
}
break;
case CMD_IP_REACHABILITY_LOST:
if (msg.obj != null) {
sb.append(" ").append((String) msg.obj);
}
break;
case CMD_INSTALL_PACKET_FILTER:
sb.append(" len=" + ((byte[]) msg.obj).length);
break;
case CMD_SET_FALLBACK_PACKET_FILTERING:
sb.append(" enabled=" + (boolean) msg.obj);
break;
case CMD_ROAM_WATCHDOG_TIMER:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" cur=").append(mRoamWatchdogCount);
break;
case CMD_DISCONNECTING_WATCHDOG_TIMER:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
sb.append(" cur=").append(mDisconnectingWatchdogCount);
break;
case CMD_START_RSSI_MONITORING_OFFLOAD:
case CMD_STOP_RSSI_MONITORING_OFFLOAD:
case CMD_RSSI_THRESHOLD_BREACHED:
sb.append(" rssi=");
sb.append(Integer.toString(msg.arg1));
sb.append(" thresholds=");
sb.append(Arrays.toString(mRssiRanges));
break;
case CMD_IPV4_PROVISIONING_SUCCESS:
sb.append(" ");
sb.append(/* DhcpResults */ msg.obj);
break;
default:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
break;
}
return sb.toString();
}
@Override
protected String getWhatToString(int what) {
String s = sGetWhatToString.get(what);
if (s != null) {
return s;
}
switch (what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
s = "CMD_CHANNEL_HALF_CONNECTED";
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
s = "CMD_CHANNEL_DISCONNECTED";
break;
case CMD_CONNECT_NETWORK:
s = "CMD_CONNECT_NETWORK";
break;
case CMD_SAVE_NETWORK:
s = "CMD_SAVE_NETWORK";
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
s = "SUPPLICANT_STATE_CHANGE_EVENT";
break;
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
s = "AUTHENTICATION_FAILURE_EVENT";
break;
case WifiMonitor.SUP_REQUEST_IDENTITY:
s = "SUP_REQUEST_IDENTITY";
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
s = "NETWORK_CONNECTION_EVENT";
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
s = "NETWORK_DISCONNECTION_EVENT";
break;
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
s = "ASSOCIATION_REJECTION_EVENT";
break;
case WifiMonitor.ANQP_DONE_EVENT:
s = "ANQP_DONE_EVENT";
break;
case WifiMonitor.RX_HS20_ANQP_ICON_EVENT:
s = "RX_HS20_ANQP_ICON_EVENT";
break;
case WifiMonitor.GAS_QUERY_DONE_EVENT:
s = "GAS_QUERY_DONE_EVENT";
break;
case WifiMonitor.HS20_REMEDIATION_EVENT:
s = "HS20_REMEDIATION_EVENT";
break;
case WifiMonitor.GAS_QUERY_START_EVENT:
s = "GAS_QUERY_START_EVENT";
break;
case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
s = "GROUP_CREATING_TIMED_OUT";
break;
case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
s = "P2P_CONNECTION_CHANGED";
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
s = "DISCONNECT_WIFI_REQUEST";
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
s = "DISCONNECT_WIFI_RESPONSE";
break;
case WifiP2pServiceImpl.SET_MIRACAST_MODE:
s = "SET_MIRACAST_MODE";
break;
case WifiP2pServiceImpl.BLOCK_DISCOVERY:
s = "BLOCK_DISCOVERY";
break;
case CMD_PKT_CNT_FETCH:
s = "CMD_PKT_CNT_FETCH";
break;
default:
s = "what:" + Integer.toString(what);
break;
}
return s;
}
private void handleScreenStateChanged(boolean screenOn) {
mScreenOn = screenOn;
if (mVerboseLoggingEnabled) {
logd(" handleScreenStateChanged Enter: screenOn=" + screenOn
+ " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
+ " state " + getCurrentState().getName());
}
enableRssiPolling(screenOn);
if (mUserWantsSuspendOpt.get()) {
int shouldReleaseWakeLock = 0;
if (screenOn) {
sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, shouldReleaseWakeLock);
} else {
if (isConnected()) {
// Allow 2s for suspend optimizations to be set
mSuspendWakeLock.acquire(2000);
shouldReleaseWakeLock = 1;
}
sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, shouldReleaseWakeLock);
}
}
getWifiLinkLayerStats();
mOnTimeScreenStateChange = mOnTime;
mLastScreenStateChangeTimeStamp = mLastLinkLayerStatsUpdate;
mWifiMetrics.setScreenState(screenOn);
mWifiConnectivityManager.handleScreenStateChanged(screenOn);
mNetworkFactory.handleScreenStateChanged(screenOn);
WifiLockManager wifiLockManager = mWifiInjector.getWifiLockManager();
if (wifiLockManager == null) {
Log.w(TAG, "WifiLockManager not initialized, skipping screen state notification");
} else {
wifiLockManager.handleScreenStateChanged(screenOn);
}
mSarManager.handleScreenStateChanged(screenOn);
if (mVerboseLoggingEnabled) log("handleScreenStateChanged Exit: " + screenOn);
}
private boolean checkAndSetConnectivityInstance() {
if (mCm == null) {
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
if (mCm == null) {
Log.e(TAG, "Cannot retrieve connectivity service");
return false;
}
return true;
}
private void setSuspendOptimizationsNative(int reason, boolean enabled) {
if (mVerboseLoggingEnabled) {
log("setSuspendOptimizationsNative: " + reason + " " + enabled
+ " -want " + mUserWantsSuspendOpt.get()
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
}
//mWifiNative.setSuspendOptimizations(enabled);
if (enabled) {
mSuspendOptNeedsDisabled &= ~reason;
/* None of dhcp, screen or highperf need it disabled and user wants it enabled */
if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
if (mVerboseLoggingEnabled) {
log("setSuspendOptimizationsNative do it " + reason + " " + enabled
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
}
mWifiNative.setSuspendOptimizations(mInterfaceName, true);
}
} else {
mSuspendOptNeedsDisabled |= reason;
mWifiNative.setSuspendOptimizations(mInterfaceName, false);
}
}
/**
* Makes a record of the user intent about suspend optimizations.
*/
private void setSuspendOptimizations(int reason, boolean enabled) {
if (mVerboseLoggingEnabled) log("setSuspendOptimizations: " + reason + " " + enabled);
if (enabled) {
mSuspendOptNeedsDisabled &= ~reason;
} else {
mSuspendOptNeedsDisabled |= reason;
}
if (mVerboseLoggingEnabled) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
}
/*
* Fetch RSSI, linkspeed, and frequency on current connection
*/
private void fetchRssiLinkSpeedAndFrequencyNative() {
WifiNative.SignalPollResult pollResult = mWifiNative.signalPoll(mInterfaceName);
if (pollResult == null) {
return;
}
int newRssi = pollResult.currentRssi;
int newTxLinkSpeed = pollResult.txBitrate;
int newFrequency = pollResult.associationFrequency;
int newRxLinkSpeed = pollResult.rxBitrate;
if (mVerboseLoggingEnabled) {
logd("fetchRssiLinkSpeedAndFrequencyNative rssi=" + newRssi
+ " TxLinkspeed=" + newTxLinkSpeed + " freq=" + newFrequency
+ " RxLinkSpeed=" + newRxLinkSpeed);
}
if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
// screen out invalid values
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
if (newRssi > 0) {
Log.wtf(TAG, "Error! +ve value RSSI: " + newRssi);
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 informing others
* interested in RSSI of all the changes in signal
* level.
*/
int newSignalLevel = RssiUtil.calculateSignalLevel(mContext, newRssi);
if (newSignalLevel != mLastSignalLevel) {
updateCapabilities();
sendRssiChangeBroadcast(newRssi);
}
mLastSignalLevel = newSignalLevel;
} else {
mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
updateCapabilities();
}
/*
* set Tx link speed only if it is valid
*/
if (newTxLinkSpeed > 0) {
mWifiInfo.setLinkSpeed(newTxLinkSpeed);
mWifiInfo.setTxLinkSpeedMbps(newTxLinkSpeed);
}
/*
* set Rx link speed only if it is valid
*/
if (newRxLinkSpeed > 0) {
mWifiInfo.setRxLinkSpeedMbps(newRxLinkSpeed);
}
if (newFrequency > 0) {
mWifiInfo.setFrequency(newFrequency);
}
mWifiConfigManager.updateScanDetailCacheFromWifiInfo(mWifiInfo);
/*
* Increment various performance metrics
*/
mWifiMetrics.handlePollResult(mWifiInfo);
}
// Polling has completed, hence we won't have a score anymore
private void cleanWifiScore() {
mWifiInfo.setTxBadRate(0);
mWifiInfo.setTxSuccessRate(0);
mWifiInfo.setTxRetriesRate(0);
mWifiInfo.setRxSuccessRate(0);
mWifiScoreReport.reset();
mLastLinkLayerStats = null;
}
private void updateLinkProperties(LinkProperties newLp) {
if (mVerboseLoggingEnabled) {
log("Link configuration changed for netId: " + mLastNetworkId
+ " old: " + mLinkProperties + " new: " + newLp);
}
// We own this instance of LinkProperties because IpClient passes us a copy.
mLinkProperties = newLp;
if (mNetworkAgent != null) {
mNetworkAgent.sendLinkProperties(mLinkProperties);
}
if (getNetworkDetailedState() == DetailedState.CONNECTED) {
// If anything has changed and we're already connected, send out a notification.
// TODO: Update all callers to use NetworkCallbacks and delete this.
sendLinkConfigurationChangedBroadcast();
}
if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("updateLinkProperties nid: " + mLastNetworkId);
sb.append(" state: " + getNetworkDetailedState());
if (mLinkProperties != null) {
sb.append(" ");
sb.append(getLinkPropertiesSummary(mLinkProperties));
}
logd(sb.toString());
}
}
/**
* Clears all our link properties.
*/
private void clearLinkProperties() {
// Clear the link properties obtained from DHCP. The only caller of this
// function has already called IpClient#stop(), which clears its state.
synchronized (mDhcpResultsLock) {
if (mDhcpResults != null) {
mDhcpResults.clear();
}
}
// Now clear the merged link properties.
mLinkProperties.clear();
if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
}
private void sendRssiChangeBroadcast(final int newRssi) {
mBatteryStatsManager.noteWifiRssiChanged(newRssi);
StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED,
RssiUtil.calculateSignalLevel(mContext, newRssi));
Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.ACCESS_WIFI_STATE);
}
private void sendNetworkStateChangeBroadcast(String bssid) {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
networkInfo.setExtraInfo(null);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
//TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendLinkConfigurationChangedBroadcast() {
Intent intent = new Intent(WifiManager.ACTION_LINK_CONFIGURATION_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
* Helper method used to send state about supplicant - This is NOT information about the current
* wifi connection state.
*
* TODO: b/79504296 This broadcast has been deprecated and should be removed
*/
private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
* Record the detailed state of a network.
*
* @param state the new {@code DetailedState}
*/
private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
boolean hidden = false;
if (mIsAutoRoaming) {
// There is generally a confusion in the system about colluding
// WiFi Layer 2 state (as reported by supplicant) and the Network state
// which leads to multiple confusion.
//
// If link is roaming, we already have an IP address
// as well we were connected and are doing L2 cycles of
// reconnecting or renewing IP address to check that we still have it
// This L2 link flapping should ne be reflected into the Network state
// which is the state of the WiFi Network visible to Layer 3 and applications
// Note that once roaming is completed, we will
// set the Network state to where it should be, or leave it as unchanged
//
hidden = true;
}
if (mVerboseLoggingEnabled) {
log("setDetailed state, old ="
+ mNetworkInfo.getDetailedState() + " and new state=" + state
+ " hidden=" + hidden);
}
if (hidden) {
return false;
}
if (state != mNetworkInfo.getDetailedState()) {
mNetworkInfo.setDetailedState(state, null, null);
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
}
sendNetworkStateChangeBroadcast(null);
return true;
}
return false;
}
private DetailedState getNetworkDetailedState() {
return mNetworkInfo.getDetailedState();
}
private SupplicantState handleSupplicantStateChange(Message message) {
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, 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)
mWifiInfo.setSupplicantState(state);
// Network id and SSID are only valid when we start connecting
if (SupplicantState.isConnecting(state)) {
mWifiInfo.setNetworkId(stateChangeResult.networkId);
mWifiInfo.setBSSID(stateChangeResult.BSSID);
mWifiInfo.setSSID(stateChangeResult.wifiSsid);
if (state == SupplicantState.ASSOCIATED) {
mWifiInfo.setWifiStandard(mWifiNative.getWifiStandard(mInterfaceName));
}
} else {
// Reset parameters according to WifiInfo.reset()
mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
mWifiInfo.setBSSID(null);
mWifiInfo.setSSID(null);
mWifiInfo.setWifiStandard(ScanResult.WIFI_STANDARD_UNKNOWN);
}
updateL2KeyAndGroupHint();
// SSID might have been updated, so call updateCapabilities
updateCapabilities();
final WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
mWifiInfo.setEphemeral(config.ephemeral);
mWifiInfo.setTrusted(config.trusted);
mWifiInfo.setOsuAp(config.osu);
if (config.fromWifiNetworkSpecifier || config.fromWifiNetworkSuggestion) {
mWifiInfo.setAppPackageName(config.creatorName);
}
// Set meteredHint if scan result says network is expensive
ScanDetailCache scanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(
config.networkId);
if (scanDetailCache != null) {
ScanDetail scanDetail = scanDetailCache.getScanDetail(stateChangeResult.BSSID);
if (scanDetail != null) {
mWifiInfo.setFrequency(scanDetail.getScanResult().frequency);
NetworkDetail networkDetail = scanDetail.getNetworkDetail();
if (networkDetail != null
&& networkDetail.getAnt() == NetworkDetail.Ant.ChargeablePublic) {
mWifiInfo.setMeteredHint(true);
}
}
}
}
mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
return state;
}
/**
* Tells IpClient what L2Key and GroupHint to use for IpMemoryStore.
*/
private void updateL2KeyAndGroupHint() {
if (mIpClient != null) {
Pair<String, String> p = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo);
if (!p.equals(mLastL2KeyAndGroupHint)) {
if (mIpClient.setL2KeyAndGroupHint(p.first, p.second)) {
mLastL2KeyAndGroupHint = p;
} else {
mLastL2KeyAndGroupHint = null;
}
}
}
}
private @Nullable Pair<String, String> mLastL2KeyAndGroupHint = null;
/**
* Resets the Wi-Fi Connections by clearing any state, resetting any sockets
* using the interface, stopping DHCP & disabling interface
*/
private void handleNetworkDisconnect() {
if (mVerboseLoggingEnabled) {
log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
}
WifiConfiguration wifiConfig = getCurrentWifiConfiguration();
if (wifiConfig != null) {
ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromWifiConfiguration(wifiConfig);
mWifiInjector.getWakeupController().setLastDisconnectInfo(matchInfo);
mWifiNetworkSuggestionsManager.handleDisconnect(wifiConfig, getCurrentBSSID());
}
stopRssiMonitoringOffload();
clearTargetBssid("handleNetworkDisconnect");
stopIpClient();
/* Reset data structures */
mWifiScoreReport.reset();
mWifiInfo.reset();
/* Reset roaming parameters */
mIsAutoRoaming = false;
setNetworkDetailedState(DetailedState.DISCONNECTED);
synchronized (mNetworkAgentLock) {
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent = null;
}
}
/* Clear network properties */
clearLinkProperties();
/* Cend event to CM & network change broadcast */
sendNetworkStateChangeBroadcast(mLastBssid);
mLastBssid = null;
mLastLinkLayerStats = null;
registerDisconnected();
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mWifiScoreCard.resetConnectionState();
updateL2KeyAndGroupHint();
}
void handlePreDhcpSetup() {
if (!mBluetoothConnectionActive) {
/*
* There are problems setting the Wi-Fi driver's power
* mode to active when bluetooth coexistence mode is
* enabled or sense.
* <p>
* We set Wi-Fi to active mode when
* obtaining an IP address because we've found
* compatibility issues with some routers with low power
* mode.
* <p>
* In order for this active power mode to properly be set,
* we disable coexistence mode until we're done with
* obtaining an IP address. One exception is if we
* are currently connected to a headset, since disabling
* coexistence would interrupt that connection.
*/
// Disable the coexistence mode
mWifiNative.setBluetoothCoexistenceMode(
mInterfaceName, WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
// Disable power save and suspend optimizations during DHCP
// Note: The order here is important for now. Brcm driver changes
// power settings when we control suspend mode optimizations.
// TODO: Remove this comment when the driver is fixed.
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
setPowerSave(false);
// Update link layer stats
getWifiLinkLayerStats();
if (mWifiP2pChannel != null) {
/* P2p discovery breaks dhcp, shut it down in order to get through this */
Message msg = new Message();
msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
msg.arg1 = WifiP2pServiceImpl.ENABLED;
msg.arg2 = CMD_PRE_DHCP_ACTION_COMPLETE;
msg.obj = ClientModeImpl.this;
mWifiP2pChannel.sendMessage(msg);
} else {
// If the p2p service is not running, we can proceed directly.
sendMessage(CMD_PRE_DHCP_ACTION_COMPLETE);
}
}
void handlePostDhcpSetup() {
/* Restore power save and suspend optimizations */
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
setPowerSave(true);
p2pSendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
// Set the coexistence mode back to its default value
mWifiNative.setBluetoothCoexistenceMode(
mInterfaceName, WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
}
/**
* Set power save mode
*
* @param ps true to enable power save (default behavior)
* false to disable power save.
* @return true for success, false for failure
*/
public boolean setPowerSave(boolean ps) {
if (mInterfaceName != null) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Setting power save for: " + mInterfaceName + " to: " + ps);
}
mWifiNative.setPowerSave(mInterfaceName, ps);
} else {
Log.e(TAG, "Failed to setPowerSave, interfaceName is null");
return false;
}
return true;
}
/**
* Set low latency mode
*
* @param enabled true to enable low latency
* false to disable low latency (default behavior).
* @return true for success, false for failure
*/
public boolean setLowLatencyMode(boolean enabled) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "Setting low latency mode to " + enabled);
}
if (!mWifiNative.setLowLatencyMode(enabled)) {
Log.e(TAG, "Failed to setLowLatencyMode");
return false;
}
return true;
}
@VisibleForTesting
public static final long DIAGS_CONNECT_TIMEOUT_MILLIS = 60 * 1000;
/**
* Inform other components that a new connection attempt is starting.
*/
private void reportConnectionAttemptStart(
WifiConfiguration config, String targetBSSID, int roamType) {
mWifiMetrics.startConnectionEvent(config, targetBSSID, roamType);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_STARTED);
mWrongPasswordNotifier.onNewConnectionAttempt();
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
sendMessageDelayed(CMD_DIAGS_CONNECT_TIMEOUT, DIAGS_CONNECT_TIMEOUT_MILLIS);
}
private void handleConnectionAttemptEndForDiagnostics(int level2FailureCode) {
switch (level2FailureCode) {
case WifiMetrics.ConnectionEvent.FAILURE_NONE:
break;
case WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED:
// WifiDiagnostics doesn't care about pre-empted connections, or cases
// where we failed to initiate a connection attempt with supplicant.
break;
default:
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED);
}
}
/**
* Inform other components (WifiMetrics, WifiDiagnostics, WifiConnectivityManager, etc.) that
* the current connection attempt has concluded.
*/
private void reportConnectionAttemptEnd(int level2FailureCode, int connectivityFailureCode,
int level2FailureReason) {
if (level2FailureCode != WifiMetrics.ConnectionEvent.FAILURE_NONE) {
mWifiScoreCard.noteConnectionFailure(mWifiInfo,
level2FailureCode, connectivityFailureCode);
String bssid = mLastBssid == null ? mTargetRoamBSSID : mLastBssid;
String ssid = mWifiInfo.getSSID();
if (WifiManager.UNKNOWN_SSID.equals(ssid)) {
ssid = getTargetSsid();
}
int bssidBlocklistMonitorReason = convertToBssidBlocklistMonitorFailureReason(
level2FailureCode, level2FailureReason);
if (bssidBlocklistMonitorReason != -1) {
mBssidBlocklistMonitor.handleBssidConnectionFailure(bssid, ssid,
bssidBlocklistMonitorReason);
}
}
// if connected, this should be non-null.
WifiConfiguration configuration = getCurrentWifiConfiguration();
if (configuration == null) {
// If not connected, this should be non-null.
configuration = getTargetWifiConfiguration();
}
mWifiMetrics.endConnectionEvent(level2FailureCode, connectivityFailureCode,
level2FailureReason);
mWifiConnectivityManager.handleConnectionAttemptEnded(level2FailureCode);
if (configuration != null) {
mNetworkFactory.handleConnectionAttemptEnded(level2FailureCode, configuration);
mWifiNetworkSuggestionsManager.handleConnectionAttemptEnded(
level2FailureCode, configuration, getCurrentBSSID());
}
handleConnectionAttemptEndForDiagnostics(level2FailureCode);
}
private int convertToBssidBlocklistMonitorFailureReason(
int level2FailureCode, int failureReason) {
switch (level2FailureCode) {
case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT:
return BssidBlocklistMonitor.REASON_ASSOCIATION_TIMEOUT;
case WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION:
if (failureReason == WifiMetricsProto.ConnectionEvent
.ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA) {
return BssidBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA;
}
return BssidBlocklistMonitor.REASON_ASSOCIATION_REJECTION;
case WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE:
if (failureReason == WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD) {
return BssidBlocklistMonitor.REASON_WRONG_PASSWORD;
} else if (failureReason == WifiMetricsProto.ConnectionEvent
.AUTH_FAILURE_EAP_FAILURE) {
return BssidBlocklistMonitor.REASON_EAP_FAILURE;
}
return BssidBlocklistMonitor.REASON_AUTHENTICATION_FAILURE;
case WifiMetrics.ConnectionEvent.FAILURE_DHCP:
return BssidBlocklistMonitor.REASON_DHCP_FAILURE;
default:
return -1;
}
}
private void handleIPv4Success(DhcpResults dhcpResults) {
if (mVerboseLoggingEnabled) {
logd("handleIPv4Success <" + dhcpResults.toString() + ">");
logd("link address " + dhcpResults.ipAddress);
}
Inet4Address addr;
synchronized (mDhcpResultsLock) {
mDhcpResults = dhcpResults;
addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
}
if (mIsAutoRoaming) {
int previousAddress = mWifiInfo.getIpAddress();
int newAddress = Inet4AddressUtils.inet4AddressToIntHTL(addr);
if (previousAddress != newAddress) {
logd("handleIPv4Success, roaming and address changed"
+ mWifiInfo + " got: " + addr);
}
}
mWifiInfo.setInetAddress(addr);
final WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
mWifiInfo.setEphemeral(config.ephemeral);
mWifiInfo.setTrusted(config.trusted);
mWifiConfigManager.updateRandomizedMacExpireTime(config,
dhcpResults.getLeaseDuration());
mBssidBlocklistMonitor.handleDhcpProvisioningSuccess(mLastBssid, mWifiInfo.getSSID());
}
// Set meteredHint if DHCP result says network is metered
if (dhcpResults.hasMeteredHint()) {
mWifiInfo.setMeteredHint(true);
}
updateCapabilities(config);
}
private void handleSuccessfulIpConfiguration() {
mLastSignalLevel = -1; // Force update of signal strength
WifiConfiguration c = getCurrentWifiConfiguration();
if (c != null) {
// Reset IP failure tracking
c.getNetworkSelectionStatus().clearDisableReasonCounter(
WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
// Tell the framework whether the newly connected network is trusted or untrusted.
updateCapabilities(c);
}
mWifiScoreCard.noteIpConfiguration(mWifiInfo);
}
private void handleIPv4Failure() {
// TODO: Move this to provisioning failure, not DHCP failure.
// DHCPv4 failure is expected on an IPv6-only network.
mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_DHCP_FAILURE);
if (mVerboseLoggingEnabled) {
int count = -1;
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
count = config.getNetworkSelectionStatus().getDisableReasonCounter(
WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
}
log("DHCP failure count=" + count);
}
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_DHCP,
WifiMetricsProto.ConnectionEvent.HLF_DHCP,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
synchronized (mDhcpResultsLock) {
if (mDhcpResults != null) {
mDhcpResults.clear();
}
}
if (mVerboseLoggingEnabled) {
logd("handleIPv4Failure");
}
}
private void handleIpConfigurationLost() {
mWifiInfo.setInetAddress(null);
mWifiInfo.setMeteredHint(false);
mWifiConfigManager.updateNetworkSelectionStatus(mLastNetworkId,
WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
/* DHCP times out after about 30 seconds, we do a
* disconnect thru supplicant, we will let autojoin retry connecting to the network
*/
mWifiNative.disconnect(mInterfaceName);
}
private void handleIpReachabilityLost() {
mWifiScoreCard.noteIpReachabilityLost(mWifiInfo);
mWifiInfo.setInetAddress(null);
mWifiInfo.setMeteredHint(false);
// Disconnect via supplicant, and let autojoin retry connecting to the network.
mWifiNative.disconnect(mInterfaceName);
}
/*
* Read a MAC address in /proc/arp/table, used by ClientModeImpl
* so as to record MAC address of default gateway.
**/
private String macAddressFromRoute(String ipAddress) {
String macAddress = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("/proc/net/arp"));
// Skip over the line bearing column titles
String line = reader.readLine();
while ((line = reader.readLine()) != null) {
String[] tokens = line.split("[ ]+");
if (tokens.length < 6) {
continue;
}
// ARP column format is
// Address HWType HWAddress Flags Mask IFace
String ip = tokens[0];
String mac = tokens[3];
if (ipAddress.equals(ip)) {
macAddress = mac;
break;
}
}
if (macAddress == null) {
loge("Did not find remoteAddress {" + ipAddress + "} in /proc/net/arp");
}
} catch (FileNotFoundException e) {
loge("Could not open /proc/net/arp to lookup mac address");
} catch (IOException e) {
loge("Could not read /proc/net/arp to lookup mac address");
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
// Do nothing
}
}
return macAddress;
}
/**
* Determine if the specified auth failure is considered to be a permanent wrong password
* failure. The criteria for such failure is when wrong password error is detected
* and the network had never been connected before.
*
* For networks that have previously connected successfully, we consider wrong password
* failures to be temporary, to be on the conservative side. Since this might be the
* case where we are trying to connect to a wrong network (e.g. A network with same SSID
* but different password).
*/
private boolean isPermanentWrongPasswordFailure(int networkId, int reasonCode) {
if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD) {
return false;
}
WifiConfiguration network = mWifiConfigManager.getConfiguredNetwork(networkId);
if (network != null && network.getNetworkSelectionStatus().getHasEverConnected()) {
return false;
}
return true;
}
void registerNetworkFactory() {
if (!checkAndSetConnectivityInstance()) return;
mNetworkFactory.register();
mUntrustedNetworkFactory.register();
}
/**
* ClientModeImpl needs to enable/disable other services when wifi is in client mode. This
* method allows ClientModeImpl to get these additional system services.
*
* At this time, this method is used to setup variables for P2P service and Wifi Aware.
*/
private void getAdditionalWifiServiceInterfaces() {
// First set up Wifi Direct
if (mP2pSupported) {
IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
WifiP2pServiceImpl wifiP2pServiceImpl =
(WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
if (wifiP2pServiceImpl != null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),
wifiP2pServiceImpl.getP2pStateMachineMessenger());
}
}
}
/**
* Dynamically change the MAC address to use the locally randomized
* MAC address generated for each network.
* @param config WifiConfiguration with mRandomizedMacAddress to change into. If the address
* is masked out or not set, it will generate a new random MAC address.
*/
private void configureRandomizedMacAddress(WifiConfiguration config) {
if (config == null) {
Log.e(TAG, "No config to change MAC address to");
return;
}
String currentMacString = mWifiNative.getMacAddress(mInterfaceName);
MacAddress currentMac = currentMacString == null ? null :
MacAddress.fromString(currentMacString);
MacAddress newMac = mWifiConfigManager.getRandomizedMacAndUpdateIfNeeded(config);
if (!WifiConfiguration.isValidMacAddressForRandomization(newMac)) {
Log.wtf(TAG, "Config generated an invalid MAC address");
} else if (newMac.equals(currentMac)) {
Log.d(TAG, "No changes in MAC address");
} else {
mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config);
boolean setMacSuccess =
mWifiNative.setMacAddress(mInterfaceName, newMac);
Log.d(TAG, "ConnectedMacRandomization SSID(" + config.getPrintableSsid()
+ "). setMacAddress(" + newMac.toString() + ") from "
+ currentMacString + " = " + setMacSuccess);
}
}
/**
* Sets the current MAC to the factory MAC address.
*/
private void setCurrentMacToFactoryMac(WifiConfiguration config) {
MacAddress factoryMac = mWifiNative.getFactoryMacAddress(mInterfaceName);
if (factoryMac == null) {
Log.e(TAG, "Fail to set factory MAC address. Factory MAC is null.");
return;
}
String currentMacStr = mWifiNative.getMacAddress(mInterfaceName);
if (!TextUtils.equals(currentMacStr, factoryMac.toString())) {
if (mWifiNative.setMacAddress(mInterfaceName, factoryMac)) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config);
} else {
Log.e(TAG, "Failed to set MAC address to " + "'" + factoryMac.toString() + "'");
}
}
}
/**
* Helper method to check if Connected MAC Randomization is supported - onDown events are
* skipped if this feature is enabled (b/72459123).
*
* @return boolean true if Connected MAC randomization is supported, false otherwise
*/
public boolean isConnectedMacRandomizationEnabled() {
return mContext.getResources().getBoolean(
R.bool.config_wifi_connected_mac_randomization_supported);
}
/**
* Helper method allowing ClientModeManager to report an error (interface went down) and trigger
* recovery.
*
* @param reason int indicating the SelfRecovery failure type.
*/
public void failureDetected(int reason) {
// report a failure
mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_STA_IFACE_DOWN);
}
/********************************************************
* HSM states
*******************************************************/
class DefaultState extends State {
@Override
public boolean processMessage(Message message) {
boolean handleStatus = HANDLED;
int callbackIdentifier = -1;
int netId;
boolean ok;
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
AsyncChannel ac = (AsyncChannel) message.obj;
if (ac == mWifiP2pChannel) {
if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
p2pSendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
// TODO: We should probably do some cleanup or attempt a retry
// b/34283611
loge("WifiP2pService connection failure, error=" + message.arg1);
}
} else {
loge("got HALF_CONNECTED for unknown channel");
}
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
AsyncChannel ac = (AsyncChannel) message.obj;
if (ac == mWifiP2pChannel) {
loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
//TODO: Re-establish connection to state machine after a delay (b/34283611)
// mWifiP2pChannel.connect(mContext, getHandler(),
// mWifiP2pManager.getMessenger());
}
break;
}
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive =
(message.arg1 != BluetoothAdapter.STATE_DISCONNECTED);
mWifiConnectivityManager.setBluetoothConnected(mBluetoothConnectionActive);
break;
case CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
break;
case CMD_SET_HIGH_PERF_MODE:
if (message.arg1 == 1) {
setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
} else {
setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
}
break;
case CMD_INITIALIZE:
ok = mWifiNative.initialize();
replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_BOOT_COMPLETED:
// get other services that we need to manage
getAdditionalWifiServiceInterfaces();
registerNetworkFactory();
mSarManager.handleBootCompleted();
break;
case CMD_SCREEN_STATE_CHANGED:
handleScreenStateChanged(message.arg1 != 0);
break;
case CMD_DISCONNECT:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
case WifiMonitor.NETWORK_CONNECTION_EVENT:
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
case CMD_RSSI_POLL:
case CMD_ONESHOT_RSSI_POLL:
case CMD_PRE_DHCP_ACTION:
case CMD_PRE_DHCP_ACTION_COMPLETE:
case CMD_POST_DHCP_ACTION:
case WifiMonitor.SUP_REQUEST_IDENTITY:
case WifiMonitor.SUP_REQUEST_SIM_AUTH:
case WifiMonitor.TARGET_BSSID_EVENT:
case CMD_START_CONNECT:
case CMD_START_ROAM:
case WifiMonitor.ASSOCIATED_BSSID_EVENT:
case CMD_UNWANTED_NETWORK:
case CMD_DISCONNECTING_WATCHDOG_TIMER:
case CMD_ROAM_WATCHDOG_TIMER:
case CMD_SET_OPERATIONAL_MODE:
// using the CMD_SET_OPERATIONAL_MODE (sent at front of queue) to trigger the
// state transitions performed in setOperationalMode.
break;
case CMD_SET_SUSPEND_OPT_ENABLED:
if (message.arg1 == 1) {
if (message.arg2 == 1) {
mSuspendWakeLock.release();
}
setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
} else {
setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
}
break;
case CMD_CONNECT_NETWORK:
// wifi off, can't connect.
callbackIdentifier = message.arg2;
sendActionListenerFailure(callbackIdentifier, WifiManager.BUSY);
break;
case CMD_SAVE_NETWORK:
// wifi off, nothing more to do here.
callbackIdentifier = message.arg2;
sendActionListenerSuccess(callbackIdentifier);
break;
case CMD_PKT_CNT_FETCH:
callbackIdentifier = message.arg2;
sendTxPacketCountListenerFailure(callbackIdentifier, WifiManager.BUSY);
break;
case CMD_GET_SUPPORTED_FEATURES:
long featureSet = (mWifiNative.getSupportedFeatureSet(mInterfaceName));
replyToMessage(message, message.what, Long.valueOf(featureSet));
break;
case CMD_GET_LINK_LAYER_STATS:
// Not supported hence reply with error message
replyToMessage(message, message.what, null);
break;
case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
NetworkInfo info = (NetworkInfo) message.obj;
mP2pConnected.set(info.isConnected());
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
mTemporarilyDisconnectWifi = (message.arg1 == 1);
replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
break;
/* Link configuration (IP address, DNS, ...) changes notified via netlink */
case CMD_UPDATE_LINKPROPERTIES:
updateLinkProperties((LinkProperties) message.obj);
break;
case CMD_START_SUBSCRIPTION_PROVISIONING:
replyToMessage(message, message.what, 0);
break;
case CMD_IP_CONFIGURATION_SUCCESSFUL:
case CMD_IP_CONFIGURATION_LOST:
case CMD_IP_REACHABILITY_LOST:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_START_IP_PACKET_OFFLOAD:
/* fall-through */
case CMD_STOP_IP_PACKET_OFFLOAD:
case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF:
case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF:
if (mNetworkAgent != null) {
mNetworkAgent.onSocketKeepaliveEvent(message.arg1,
SocketKeepalive.ERROR_INVALID_NETWORK);
}
break;
case CMD_START_RSSI_MONITORING_OFFLOAD:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_STOP_RSSI_MONITORING_OFFLOAD:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_QUERY_OSU_ICON:
/* reply with arg1 = 0 - it returns API failure to the calling app
* (message.what is not looked at)
*/
replyToMessage(message, message.what);
break;
case CMD_RESET_SIM_NETWORKS:
/* Defer this message until supplicant is started. */
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
case CMD_INSTALL_PACKET_FILTER:
mWifiNative.installPacketFilter(mInterfaceName, (byte[]) message.obj);
break;
case CMD_READ_PACKET_FILTER:
byte[] data = mWifiNative.readPacketFilter(mInterfaceName);
if (mIpClient != null) {
mIpClient.readPacketFilterComplete(data);
}
break;
case CMD_SET_FALLBACK_PACKET_FILTERING:
if ((boolean) message.obj) {
mWifiNative.startFilteringMulticastV4Packets(mInterfaceName);
} else {
mWifiNative.stopFilteringMulticastV4Packets(mInterfaceName);
}
break;
case CMD_DIAGS_CONNECT_TIMEOUT:
mWifiDiagnostics.reportConnectionEvent(
BaseWifiDiagnostics.CONNECTION_EVENT_TIMEOUT);
break;
case 0:
// We want to notice any empty messages (with what == 0) that might crop up.
// For example, we may have recycled a message sent to multiple handlers.
Log.wtf(TAG, "Error! empty message encountered");
break;
default:
loge("Error! unhandled message" + message);
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
}
/**
* Helper method to start other services and get state ready for client mode
*/
private void setupClientMode() {
Log.d(TAG, "setupClientMode() ifacename = " + mInterfaceName);
setHighPerfModeEnabled(false);
mWifiStateTracker.updateState(WifiStateTracker.INVALID);
mIpClientCallbacks = new IpClientCallbacksImpl();
mFacade.makeIpClient(mContext, mInterfaceName, mIpClientCallbacks);
if (!mIpClientCallbacks.awaitCreation()) {
loge("Timeout waiting for IpClient");
}
setMulticastFilter(true);
registerForWifiMonitorEvents();
mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts();
setSupplicantLogLevel();
// reset state related to supplicant starting
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
// Initialize data structures
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
// TODO: b/79504296 This broadcast has been deprecated and should be removed
sendSupplicantConnectionChangedBroadcast(true);
mWifiNative.setExternalSim(mInterfaceName, true);
setRandomMacOui();
mCountryCode.setReadyForChange(true);
mWifiDiagnostics.startPktFateMonitoring(mInterfaceName);
mWifiDiagnostics.startLogging(mInterfaceName);
mMboOceController.enable();
/**
* Enable bluetooth coexistence scan mode when bluetooth connection is active.
* When this mode is on, some of the low-level scan parameters used by the
* driver are changed to reduce interference with bluetooth
*/
mWifiNative.setBluetoothCoexistenceScanMode(mInterfaceName, mBluetoothConnectionActive);
// initialize network state
setNetworkDetailedState(DetailedState.DISCONNECTED);
// Disable legacy multicast filtering, which on some chipsets defaults to enabled.
// Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
// provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
// IpClient.Callback.setFallbackMulticastFilter()
mWifiNative.stopFilteringMulticastV4Packets(mInterfaceName);
mWifiNative.stopFilteringMulticastV6Packets(mInterfaceName);
// Set the right suspend mode settings
mWifiNative.setSuspendOptimizations(mInterfaceName, mSuspendOptNeedsDisabled == 0
&& mUserWantsSuspendOpt.get());
setPowerSave(true);
// Disable wpa_supplicant from auto reconnecting.
mWifiNative.enableStaAutoReconnect(mInterfaceName, false);
// STA has higher priority over P2P
mWifiNative.setConcurrencyPriority(true);
}
/**
* Helper method to stop external services and clean up state from client mode.
*/
private void stopClientMode() {
// exiting supplicant started state is now only applicable to client mode
mWifiDiagnostics.stopLogging(mInterfaceName);
mMboOceController.disable();
if (mIpClient != null && mIpClient.shutdown()) {
// Block to make sure IpClient has really shut down, lest cleanup
// race with, say, bringup code over in tethering.
mIpClientCallbacks.awaitShutdown();
}
mNetworkInfo.setIsAvailable(false);
if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mCountryCode.setReadyForChange(false);
mInterfaceName = null;
// TODO: b/79504296 This broadcast has been deprecated and should be removed
sendSupplicantConnectionChangedBroadcast(false);
// Let's remove any ephemeral or passpoint networks.
mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks();
}
void registerConnected() {
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
mWifiConfigManager.updateNetworkAfterConnect(mLastNetworkId);
// Notify PasspointManager of Passpoint network connected event.
WifiConfiguration currentNetwork = getCurrentWifiConfiguration();
if (currentNetwork != null && currentNetwork.isPasspoint()) {
mPasspointManager.onPasspointNetworkConnected(currentNetwork.FQDN);
}
}
}
void registerDisconnected() {
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
mWifiConfigManager.updateNetworkAfterDisconnect(mLastNetworkId);
}
}
/**
* Returns WifiConfiguration object corresponding to the currently connected network, null if
* not connected.
*/
public WifiConfiguration getCurrentWifiConfiguration() {
if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
return null;
}
return mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
}
private WifiConfiguration getTargetWifiConfiguration() {
if (mTargetNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
return null;
}
return mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
}
ScanResult getCurrentScanResult() {
WifiConfiguration config = getCurrentWifiConfiguration();
if (config == null) {
return null;
}
String bssid = mWifiInfo.getBSSID();
if (bssid == null) {
bssid = mTargetRoamBSSID;
}
ScanDetailCache scanDetailCache =
mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache == null) {
return null;
}
return scanDetailCache.getScanResult(bssid);
}
String getCurrentBSSID() {
return mLastBssid;
}
class ConnectModeState extends State {
@Override
public void enter() {
Log.d(TAG, "entering ConnectModeState: ifaceName = " + mInterfaceName);
mOperationalMode = CONNECT_MODE;
setupClientMode();
if (!mWifiNative.removeAllNetworks(mInterfaceName)) {
loge("Failed to remove networks on entering connect mode");
}
mWifiInfo.reset();
mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
mWifiInjector.getWakeupController().reset();
mNetworkInfo.setIsAvailable(true);
if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
// initialize network state
setNetworkDetailedState(DetailedState.DISCONNECTED);
// Inform WifiConnectivityManager that Wifi is enabled
mWifiConnectivityManager.setWifiEnabled(true);
mNetworkFactory.setWifiState(true);
// Inform metrics that Wifi is Enabled (but not yet connected)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
mWifiMetrics.logStaEvent(StaEvent.TYPE_WIFI_ENABLED);
mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
}
@Override
public void exit() {
mOperationalMode = DISABLED_MODE;
// Let the system know that wifi is not available since we are exiting client mode.
mNetworkInfo.setIsAvailable(false);
if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
// Inform WifiConnectivityManager that Wifi is disabled
mWifiConnectivityManager.setWifiEnabled(false);
mNetworkFactory.setWifiState(false);
// Inform metrics that Wifi is being disabled (Toggled, airplane enabled, etc)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISABLED);
mWifiMetrics.logStaEvent(StaEvent.TYPE_WIFI_DISABLED);
// Inform scorecard that wifi is being disabled
mWifiScoreCard.noteWifiDisabled(mWifiInfo);
if (!mWifiNative.removeAllNetworks(mInterfaceName)) {
loge("Failed to remove networks on exiting connect mode");
}
mWifiInfo.reset();
mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
stopClientMode();
}
@Override
public boolean processMessage(Message message) {
WifiConfiguration config;
int netId;
boolean ok;
boolean didDisconnect;
String bssid;
String ssid;
NetworkUpdateResult result;
Set<Integer> removedNetworkIds;
int reasonCode;
boolean timedOut;
boolean handleStatus = HANDLED;
int callbackIdentifier = -1;
int level2FailureReason =
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN;
switch (message.what) {
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_ASSOC_FAILURE);
mDidBlackListBSSID = false;
bssid = (String) message.obj;
timedOut = message.arg1 > 0;
reasonCode = message.arg2;
Log.d(TAG, "Association Rejection event: bssid=" + bssid + " reason code="
+ reasonCode + " timedOut=" + Boolean.toString(timedOut));
if (bssid == null || TextUtils.isEmpty(bssid)) {
// If BSSID is null, use the target roam BSSID
bssid = mTargetRoamBSSID;
}
mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_ASSOCIATION_REJECTION);
mWifiConfigManager.setRecentFailureAssociationStatus(mTargetNetworkId,
reasonCode);
if (reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) {
level2FailureReason = WifiMetricsProto.ConnectionEvent
.ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA;
}
// If rejection occurred while Metrics is tracking a ConnnectionEvent, end it.
reportConnectionAttemptEnd(
timedOut
? WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT
: WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
level2FailureReason);
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(), bssid,
WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
break;
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_AUTH_FAILURE);
int disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_FAILURE;
reasonCode = message.arg1;
// Check if this is a permanent wrong password failure.
if (isPermanentWrongPasswordFailure(mTargetNetworkId, reasonCode)) {
disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_BY_WRONG_PASSWORD;
WifiConfiguration targetedNetwork =
mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (targetedNetwork != null) {
mWrongPasswordNotifier.onWrongPasswordError(
targetedNetwork.SSID);
}
} else if (reasonCode == WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE) {
int errorCode = message.arg2;
handleEapAuthFailure(mTargetNetworkId, errorCode);
if (errorCode == WifiNative.EAP_SIM_NOT_SUBSCRIBED) {
disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_NO_SUBSCRIPTION;
}
}
mWifiConfigManager.updateNetworkSelectionStatus(
mTargetNetworkId, disableReason);
mWifiConfigManager.clearRecentFailureReason(mTargetNetworkId);
//If failure occurred while Metrics is tracking a ConnnectionEvent, end it.
switch (reasonCode) {
case WifiManager.ERROR_AUTH_FAILURE_NONE:
level2FailureReason =
WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE;
break;
case WifiManager.ERROR_AUTH_FAILURE_TIMEOUT:
level2FailureReason =
WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT;
break;
case WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD:
level2FailureReason =
WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD;
break;
case WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE:
level2FailureReason =
WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_EAP_FAILURE;
break;
default:
level2FailureReason =
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN;
break;
}
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
level2FailureReason);
if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD) {
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(),
(mLastBssid == null) ? mTargetRoamBSSID : mLastBssid,
WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
// Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
// when authentication times out after a successful connection,
// we can figure this from the supplicant state. If supplicant
// state is DISCONNECTED, but the mNetworkInfo says we are not
// disconnected, we need to handle a disconnection
if (state == SupplicantState.DISCONNECTED
&& mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
if (mVerboseLoggingEnabled) {
log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
}
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
// If we have COMPLETED a connection to a BSSID, start doing
// DNAv4/DNAv6 -style probing for on-link neighbors of
// interest (e.g. routers); harmless if none are configured.
if (state == SupplicantState.COMPLETED) {
if (mIpClient != null) {
mIpClient.confirmConfiguration();
}
mWifiScoreReport.noteIpCheck();
}
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
if (message.arg1 == 1) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
mWifiNative.disconnect(mInterfaceName);
mTemporarilyDisconnectWifi = true;
} else {
mWifiNative.reconnect(mInterfaceName);
mTemporarilyDisconnectWifi = false;
}
break;
case WifiMonitor.SUP_REQUEST_IDENTITY:
netId = message.arg2;
boolean identitySent = false;
// For SIM & AKA/AKA' EAP method Only, get identity from ICC
if (mTargetWifiConfiguration != null
&& mTargetWifiConfiguration.networkId == netId
&& mTargetWifiConfiguration.enterpriseConfig != null
&& mTargetWifiConfiguration.enterpriseConfig
.requireSimCredential()) {
// Pair<identity, encrypted identity>
Pair<String, String> identityPair = mTelephonyUtil
.getSimIdentity(mTargetWifiConfiguration);
Log.i(TAG, "SUP_REQUEST_IDENTITY: identityPair=" + identityPair);
if (identityPair != null && identityPair.first != null) {
mWifiNative.simIdentityResponse(mInterfaceName, identityPair.first,
identityPair.second);
identitySent = true;
} else {
Log.e(TAG, "Unable to retrieve identity from Telephony");
}
}
if (!identitySent) {
// Supplicant lacks credentials to connect to that network, hence black list
ssid = (String) message.obj;
if (mTargetWifiConfiguration != null && ssid != null
&& mTargetWifiConfiguration.SSID != null
&& mTargetWifiConfiguration.SSID.equals("\"" + ssid + "\"")) {
mWifiConfigManager.updateNetworkSelectionStatus(
mTargetWifiConfiguration.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_NO_CREDENTIALS);
}
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_GENERIC);
mWifiNative.disconnect(mInterfaceName);
}
break;
case WifiMonitor.SUP_REQUEST_SIM_AUTH:
logd("Received SUP_REQUEST_SIM_AUTH");
SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
if (requestData != null) {
if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
handleGsmAuthRequest(requestData);
} else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA
|| requestData.protocol == WifiEnterpriseConfig.Eap.AKA_PRIME) {
handle3GAuthRequest(requestData);
}
} else {
loge("Invalid SIM auth request");
}
break;
case CMD_START_SUBSCRIPTION_PROVISIONING:
IProvisioningCallback callback = (IProvisioningCallback) message.obj;
OsuProvider provider =
(OsuProvider) message.getData().getParcelable(EXTRA_OSU_PROVIDER);
int res = mPasspointManager.startSubscriptionProvisioning(
message.arg1, provider, callback) ? 1 : 0;
replyToMessage(message, message.what, res);
break;
case CMD_RECONNECT:
WorkSource workSource = (WorkSource) message.obj;
mWifiConnectivityManager.forceConnectivityScan(workSource);
break;
case CMD_REASSOCIATE:
mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
mWifiNative.reassociate(mInterfaceName);
break;
case CMD_START_ROAM:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
int uid = message.arg2;
bssid = (String) message.obj;
if (!hasConnectionRequests()) {
if (mNetworkAgent == null) {
loge("CMD_START_CONNECT but no requests and not connected,"
+ " bailing");
break;
} else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
loge("CMD_START_CONNECT but no requests and connected, but app "
+ "does not have sufficient permissions, bailing");
break;
}
}
config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
logd("CMD_START_CONNECT "
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " roam=" + Boolean.toString(mIsAutoRoaming));
if (config == null) {
loge("CMD_START_CONNECT and no config, bail out...");
break;
}
// Update scorecard while there is still state from existing connection
mWifiScoreCard.noteConnectionAttempt(mWifiInfo);
mTargetNetworkId = netId;
setTargetBssid(config, bssid);
mBssidBlocklistMonitor.updateFirmwareRoamingConfiguration(config.SSID);
reportConnectionAttemptStart(config, mTargetRoamBSSID,
WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
if (config.macRandomizationSetting
== WifiConfiguration.RANDOMIZATION_PERSISTENT
&& isConnectedMacRandomizationEnabled()) {
configureRandomizedMacAddress(config);
} else {
setCurrentMacToFactoryMac(config);
}
String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
mWifiInfo.setMacAddress(currentMacAddress);
Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
if (config.enterpriseConfig != null
&& config.enterpriseConfig.requireSimCredential()
&& mWifiInjector.getCarrierNetworkConfig()
.isCarrierEncryptionInfoAvailable()
&& TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) {
String anonAtRealm = mTelephonyUtil
.getAnonymousIdentityWith3GppRealm(config);
// Use anonymous@<realm> when pseudonym is not available
config.enterpriseConfig.setAnonymousIdentity(anonAtRealm);
}
if (mWifiNative.connectToNetwork(mInterfaceName, config)) {
mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime();
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
mTargetWifiConfiguration = config;
mIsAutoRoaming = false;
if (getCurrentState() != mDisconnectedState) {
transitionTo(mDisconnectingState);
}
} else {
loge("CMD_START_CONNECT Failed to start connection to network " + config);
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
break;
}
break;
case CMD_CONNECT_NETWORK:
callbackIdentifier = message.arg2;
result = (NetworkUpdateResult) message.obj;
netId = result.getNetworkId();
connectToUserSelectNetwork(
netId, message.sendingUid, result.hasCredentialChanged());
mWifiMetrics.logStaEvent(
StaEvent.TYPE_CONNECT_NETWORK,
mWifiConfigManager.getConfiguredNetwork(netId));
sendActionListenerSuccess(callbackIdentifier);
break;
case CMD_SAVE_NETWORK:
callbackIdentifier = message.arg2;
result = (NetworkUpdateResult) message.obj;
netId = result.getNetworkId();
if (mWifiInfo.getNetworkId() == netId) {
if (result.hasCredentialChanged()) {
// The network credentials changed and we're connected to this network,
// start a new connection with the updated credentials.
logi("CMD_SAVE_NETWORK credential changed for nid="
+ netId + ". Reconnecting.");
startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
} else {
if (result.hasProxyChanged()) {
if (mIpClient != null) {
log("Reconfiguring proxy on connection");
WifiConfiguration currentConfig = getCurrentWifiConfiguration();
if (currentConfig != null) {
mIpClient.setHttpProxy(currentConfig.getHttpProxy());
} else {
Log.w(TAG,
"CMD_SAVE_NETWORK proxy change - but no current "
+ "Wi-Fi config");
}
}
}
if (result.hasIpChanged()) {
// The current connection configuration was changed
// We switched from DHCP to static or from static to DHCP, or the
// static IP address has changed.
log("Reconfiguring IP on connection");
transitionTo(mObtainingIpState);
}
}
} else if (mWifiInfo.getNetworkId() == WifiConfiguration.INVALID_NETWORK_ID
&& result.hasCredentialChanged()) {
logi("CMD_SAVE_NETWORK credential changed for nid="
+ netId + " while disconnected. Connecting.");
startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
}
sendActionListenerSuccess(callbackIdentifier);
break;
case WifiMonitor.ASSOCIATED_BSSID_EVENT:
// This is where we can confirm the connection BSSID. Use it to find the
// right ScanDetail to populate metrics.
String someBssid = (String) message.obj;
if (someBssid != null) {
// Get the ScanDetail associated with this BSSID.
ScanDetailCache scanDetailCache =
mWifiConfigManager.getScanDetailCacheForNetwork(mTargetNetworkId);
if (scanDetailCache != null) {
mWifiMetrics.setConnectionScanDetail(scanDetailCache.getScanDetail(
someBssid));
}
// Update last associated BSSID
mLastBssid = someBssid;
}
handleStatus = NOT_HANDLED;
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (mVerboseLoggingEnabled) log("Network connection established");
mLastNetworkId = message.arg1;
mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
mLastBssid = (String) message.obj;
reasonCode = message.arg2;
// TODO: This check should not be needed after ClientModeImpl refactor.
// Currently, the last connected network configuration is left in
// wpa_supplicant, this may result in wpa_supplicant initiating connection
// to it after a config store reload. Hence the old network Id lookups may not
// work, so disconnect the network and let network selector reselect a new
// network.
config = getCurrentWifiConfiguration();
if (config != null) {
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
ScanDetailCache scanDetailCache =
mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache != null && mLastBssid != null) {
ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
if (scanResult != null) {
mWifiInfo.setFrequency(scanResult.frequency);
}
}
// We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
if (config.enterpriseConfig != null
&& config.enterpriseConfig.requireSimCredential()) {
mLastSubId = mTelephonyUtil.getBestMatchSubscriptionId(config);
String anonymousIdentity =
mWifiNative.getEapAnonymousIdentity(mInterfaceName);
if (!TextUtils.isEmpty(anonymousIdentity)
&& !TelephonyUtil
.isAnonymousAtRealmIdentity(anonymousIdentity)) {
String decoratedPseudonym = mTelephonyUtil
.decoratePseudonymWith3GppRealm(config,
anonymousIdentity);
if (decoratedPseudonym != null) {
anonymousIdentity = decoratedPseudonym;
}
if (mVerboseLoggingEnabled) {
log("EAP Pseudonym: " + anonymousIdentity);
}
// Save the pseudonym only if it is a real one
config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
} else {
// Clear any stored pseudonyms
config.enterpriseConfig.setAnonymousIdentity(null);
}
mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
}
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
} else {
logw("Connected to unknown networkId " + mLastNetworkId
+ ", disconnecting...");
sendMessage(CMD_DISCONNECT);
}
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
// Calling handleNetworkDisconnect here is redundant because we might already
// have called it when leaving L2ConnectedState to go to disconnecting state
// or thru other path
// We should normally check the mWifiInfo or mLastNetworkId so as to check
// if they are valid, and only in this case call handleNEtworkDisconnect,
// TODO: this should be fixed for a L MR release
// The side effect of calling handleNetworkDisconnect twice is that a bunch of
// idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
// at the chip etc...
if (mVerboseLoggingEnabled) log("ConnectModeState: Network connection lost ");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
case CMD_QUERY_OSU_ICON:
mPasspointManager.queryPasspointIcon(
((Bundle) message.obj).getLong(EXTRA_OSU_ICON_QUERY_BSSID),
((Bundle) message.obj).getString(EXTRA_OSU_ICON_QUERY_FILENAME));
break;
case WifiMonitor.TARGET_BSSID_EVENT:
// Trying to associate to this BSSID
if (message.obj != null) {
mTargetRoamBSSID = (String) message.obj;
}
break;
case CMD_GET_LINK_LAYER_STATS:
WifiLinkLayerStats stats = getWifiLinkLayerStats();
replyToMessage(message, message.what, stats);
break;
case CMD_RESET_SIM_NETWORKS:
log("resetting EAP-SIM/AKA/AKA' networks since SIM was changed");
boolean simPresent = message.arg1 == 1;
if (!simPresent) {
mWifiConfigManager.resetSimNetworks();
}
break;
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive = (message.arg1
!= BluetoothAdapter.STATE_DISCONNECTED);
mWifiNative.setBluetoothCoexistenceScanMode(
mInterfaceName, mBluetoothConnectionActive);
mWifiConnectivityManager.setBluetoothConnected(mBluetoothConnectionActive);
break;
case CMD_SET_SUSPEND_OPT_ENABLED:
if (message.arg1 == 1) {
setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
if (message.arg2 == 1) {
mSuspendWakeLock.release();
}
} else {
setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
}
break;
case CMD_SET_HIGH_PERF_MODE:
if (message.arg1 == 1) {
setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
} else {
setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
}
break;
case CMD_ENABLE_TDLS:
if (message.obj != null) {
String remoteAddress = (String) message.obj;
boolean enable = (message.arg1 == 1);
mWifiNative.startTdls(mInterfaceName, remoteAddress, enable);
}
break;
case WifiMonitor.ANQP_DONE_EVENT:
// TODO(zqiu): remove this when switch over to wificond for ANQP requests.
mPasspointManager.notifyANQPDone((AnqpEvent) message.obj);
break;
case CMD_STOP_IP_PACKET_OFFLOAD: {
int slot = message.arg1;
int ret = stopWifiIPPacketOffload(slot);
if (mNetworkAgent != null) {
mNetworkAgent.onSocketKeepaliveEvent(slot, ret);
}
break;
}
case WifiMonitor.RX_HS20_ANQP_ICON_EVENT:
// TODO(zqiu): remove this when switch over to wificond for icon requests.
mPasspointManager.notifyIconDone((IconEvent) message.obj);
break;
case WifiMonitor.HS20_REMEDIATION_EVENT:
// TODO(zqiu): remove this when switch over to wificond for WNM frames
// monitoring.
mPasspointManager.receivedWnmFrame((WnmData) message.obj);
break;
case CMD_CONFIG_ND_OFFLOAD:
final boolean enabled = (message.arg1 > 0);
mWifiNative.configureNeighborDiscoveryOffload(mInterfaceName, enabled);
break;
case CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER:
mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
}
private WifiNetworkAgentSpecifier createNetworkAgentSpecifier(
@NonNull WifiConfiguration currentWifiConfiguration, @Nullable String currentBssid,
int specificRequestUid, @NonNull String specificRequestPackageName) {
currentWifiConfiguration.BSSID = currentBssid;
WifiNetworkAgentSpecifier wns =
new WifiNetworkAgentSpecifier(currentWifiConfiguration, specificRequestUid,
specificRequestPackageName);
return wns;
}
private NetworkCapabilities getCapabilities(WifiConfiguration currentWifiConfiguration) {
final NetworkCapabilities result = new NetworkCapabilities(mNetworkCapabilitiesFilter);
// MatchAllNetworkSpecifier set in the mNetworkCapabilitiesFilter should never be set in the
// agent's specifier.
result.setNetworkSpecifier(null);
if (currentWifiConfiguration == null) {
return result;
}
if (!mWifiInfo.isTrusted()) {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
} else {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
}
if (!WifiConfiguration.isMetered(currentWifiConfiguration, mWifiInfo)) {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI) {
result.setSignalStrength(mWifiInfo.getRssi());
} else {
result.setSignalStrength(NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED);
}
if (currentWifiConfiguration.osu) {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
if (!mWifiInfo.getSSID().equals(WifiManager.UNKNOWN_SSID)) {
result.setSSID(mWifiInfo.getSSID());
} else {
result.setSSID(null);
}
Pair<Integer, String> specificRequestUidAndPackageName =
mNetworkFactory.getSpecificNetworkRequestUidAndPackageName(
currentWifiConfiguration);
// There is an active specific request.
if (specificRequestUidAndPackageName.first != Process.INVALID_UID) {
// Remove internet capability.
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
// Fill up the network agent specifier for this connection.
result.setNetworkSpecifier(
createNetworkAgentSpecifier(
currentWifiConfiguration, getCurrentBSSID(),
specificRequestUidAndPackageName.first,
specificRequestUidAndPackageName.second));
return result;
}
/**
* Method to update network capabilities from the current WifiConfiguration.
*/
public void updateCapabilities() {
updateCapabilities(getCurrentWifiConfiguration());
}
private void updateCapabilities(WifiConfiguration currentWifiConfiguration) {
if (mNetworkAgent == null) {
return;
}
mNetworkAgent.sendNetworkCapabilities(getCapabilities(currentWifiConfiguration));
}
private void handleEapAuthFailure(int networkId, int errorCode) {
WifiConfiguration targetedNetwork =
mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (targetedNetwork != null) {
switch (targetedNetwork.enterpriseConfig.getEapMethod()) {
case WifiEnterpriseConfig.Eap.SIM:
case WifiEnterpriseConfig.Eap.AKA:
case WifiEnterpriseConfig.Eap.AKA_PRIME:
if (errorCode == WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED) {
mTelephonyUtil.resetCarrierKeysForImsiEncryption(targetedNetwork);
}
break;
default:
// Do Nothing
}
}
}
private class WifiNetworkAgent extends NetworkAgent {
WifiNetworkAgent(Looper l, Context c, String tag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(l, c, tag, ni, nc, lp, score, misc);
}
private int mLastNetworkStatus = -1; // To detect when the status really changes
@Override
protected void unwanted() {
// Ignore if we're not the current networkAgent.
if (this != mNetworkAgent) return;
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi unwanted score " + Integer.toString(
mWifiInfo.getScore()));
}
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISCONNECT);
}
@Override
protected void networkStatus(int status, String redirectUrl) {
if (this != mNetworkAgent) return;
if (status == mLastNetworkStatus) return;
mLastNetworkStatus = status;
if (status == NetworkAgent.INVALID_NETWORK) {
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
+ Integer.toString(mWifiInfo.getScore()));
}
unwantedNetwork(NETWORK_STATUS_UNWANTED_VALIDATION_FAILED);
} else if (status == NetworkAgent.VALID_NETWORK) {
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
+ Integer.toString(mWifiInfo.getScore()));
}
mWifiMetrics.logStaEvent(StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK);
doNetworkStatus(status);
}
}
@Override
protected void saveAcceptUnvalidated(boolean accept) {
if (this != mNetworkAgent) return;
ClientModeImpl.this.sendMessage(CMD_ACCEPT_UNVALIDATED, accept ? 1 : 0);
}
@Override
protected void startSocketKeepalive(Message msg) {
ClientModeImpl.this.sendMessage(
CMD_START_IP_PACKET_OFFLOAD, msg.arg1, msg.arg2, msg.obj);
}
@Override
protected void stopSocketKeepalive(Message msg) {
ClientModeImpl.this.sendMessage(
CMD_STOP_IP_PACKET_OFFLOAD, msg.arg1, msg.arg2, msg.obj);
}
@Override
protected void addKeepalivePacketFilter(Message msg) {
ClientModeImpl.this.sendMessage(
CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, msg.arg1, msg.arg2, msg.obj);
}
@Override
protected void removeKeepalivePacketFilter(Message msg) {
ClientModeImpl.this.sendMessage(
CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, msg.arg1, msg.arg2, msg.obj);
}
@Override
protected void setSignalStrengthThresholds(int[] thresholds) {
// 0. If there are no thresholds, or if the thresholds are invalid,
// stop RSSI monitoring.
// 1. Tell the hardware to start RSSI monitoring here, possibly adding MIN_VALUE and
// MAX_VALUE at the start/end of the thresholds array if necessary.
// 2. Ensure that when the hardware event fires, we fetch the RSSI from the hardware
// event, call mWifiInfo.setRssi() with it, and call updateCapabilities(), and then
// re-arm the hardware event. This needs to be done on the state machine thread to
// avoid race conditions. The RSSI used to re-arm the event (and perhaps also the one
// sent in the NetworkCapabilities) must be the one received from the hardware event
// received, or we might skip callbacks.
// 3. Ensure that when we disconnect, RSSI monitoring is stopped.
log("Received signal strength thresholds: " + Arrays.toString(thresholds));
if (thresholds.length == 0) {
ClientModeImpl.this.sendMessage(CMD_STOP_RSSI_MONITORING_OFFLOAD,
mWifiInfo.getRssi());
return;
}
int [] rssiVals = Arrays.copyOf(thresholds, thresholds.length + 2);
rssiVals[rssiVals.length - 2] = Byte.MIN_VALUE;
rssiVals[rssiVals.length - 1] = Byte.MAX_VALUE;
Arrays.sort(rssiVals);
byte[] rssiRange = new byte[rssiVals.length];
for (int i = 0; i < rssiVals.length; i++) {
int val = rssiVals[i];
if (val <= Byte.MAX_VALUE && val >= Byte.MIN_VALUE) {
rssiRange[i] = (byte) val;
} else {
Log.e(TAG, "Illegal value " + val + " for RSSI thresholds: "
+ Arrays.toString(rssiVals));
ClientModeImpl.this.sendMessage(CMD_STOP_RSSI_MONITORING_OFFLOAD,
mWifiInfo.getRssi());
return;
}
}
// TODO: Do we quash rssi values in this sorted array which are very close?
mRssiRanges = rssiRange;
ClientModeImpl.this.sendMessage(CMD_START_RSSI_MONITORING_OFFLOAD,
mWifiInfo.getRssi());
}
@Override
protected void preventAutomaticReconnect() {
if (this != mNetworkAgent) return;
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN);
}
}
void unwantedNetwork(int reason) {
sendMessage(CMD_UNWANTED_NETWORK, reason);
}
void doNetworkStatus(int status) {
sendMessage(CMD_NETWORK_STATUS, status);
}
class L2ConnectedState extends State {
class RssiEventHandler implements WifiNative.WifiRssiEventHandler {
@Override
public void onRssiThresholdBreached(byte curRssi) {
if (mVerboseLoggingEnabled) {
Log.e(TAG, "onRssiThresholdBreach event. Cur Rssi = " + curRssi);
}
sendMessage(CMD_RSSI_THRESHOLD_BREACHED, curRssi);
}
}
RssiEventHandler mRssiEventHandler = new RssiEventHandler();
@Override
public void enter() {
mRssiPollToken++;
if (mEnableRssiPolling) {
mLinkProbeManager.resetOnNewConnection();
sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
}
if (mNetworkAgent != null) {
loge("Have NetworkAgent when entering L2Connected");
setNetworkDetailedState(DetailedState.DISCONNECTED);
}
setNetworkDetailedState(DetailedState.CONNECTING);
final NetworkCapabilities nc = getCapabilities(getCurrentWifiConfiguration());
synchronized (mNetworkAgentLock) {
mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
"WifiNetworkAgent", mNetworkInfo, nc, mLinkProperties, 60, mNetworkMisc);
}
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to faile and the device to disconnect
clearTargetBssid("L2ConnectedState");
mCountryCode.setReadyForChange(false);
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
mWifiScoreCard.noteNetworkAgentCreated(mWifiInfo, mNetworkAgent.netId);
mBssidBlocklistMonitor.handleBssidConnectionSuccess(mLastBssid, mWifiInfo.getSSID());
}
@Override
public void exit() {
if (mIpClient != null) {
mIpClient.stop();
}
// This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
// Bug: 15347363
// For paranoia's sake, call handleNetworkDisconnect
// only if BSSID is null or last networkId
// is not invalid.
if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
if (mLastBssid != null) {
sb.append(" ").append(mLastBssid);
}
}
if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
handleNetworkDisconnect();
}
mCountryCode.setReadyForChange(true);
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
mWifiStateTracker.updateState(WifiStateTracker.DISCONNECTED);
//Inform WifiLockManager
WifiLockManager wifiLockManager = mWifiInjector.getWifiLockManager();
wifiLockManager.updateWifiClientConnected(false);
}
@Override
public boolean processMessage(Message message) {
boolean handleStatus = HANDLED;
int callbackIdentifier = -1;
switch (message.what) {
case CMD_PRE_DHCP_ACTION:
handlePreDhcpSetup();
break;
case CMD_PRE_DHCP_ACTION_COMPLETE:
if (mIpClient != null) {
mIpClient.completedPreDhcpAction();
}
break;
case CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
// We advance to mConnectedState because IpClient will also send a
// CMD_IPV4_PROVISIONING_SUCCESS message, which calls handleIPv4Success(),
// which calls updateLinkProperties, which then sends
// CMD_IP_CONFIGURATION_SUCCESSFUL.
//
// In the event of failure, we transition to mDisconnectingState
// similarly--via messages sent back from IpClient.
break;
case CMD_IPV4_PROVISIONING_SUCCESS: {
handleIPv4Success((DhcpResults) message.obj);
sendNetworkStateChangeBroadcast(mLastBssid);
break;
}
case CMD_IPV4_PROVISIONING_FAILURE: {
handleIPv4Failure();
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(),
(mLastBssid == null) ? mTargetRoamBSSID : mLastBssid,
WifiLastResortWatchdog.FAILURE_CODE_DHCP);
break;
}
case CMD_IP_CONFIGURATION_SUCCESSFUL:
if (getCurrentWifiConfiguration() == null) {
// The current config may have been removed while we were connecting,
// trigger a disconnect to clear up state.
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
} else {
handleSuccessfulIpConfiguration();
sendConnectedState();
transitionTo(mConnectedState);
}
break;
case CMD_IP_CONFIGURATION_LOST:
// Get Link layer stats so that we get fresh tx packet counters.
getWifiLinkLayerStats();
handleIpConfigurationLost();
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_DHCP,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(),
(mLastBssid == null) ? mTargetRoamBSSID : mLastBssid,
WifiLastResortWatchdog.FAILURE_CODE_DHCP);
transitionTo(mDisconnectingState);
break;
case CMD_IP_REACHABILITY_LOST:
if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST);
mWifiMetrics.logWifiIsUnusableEvent(
WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1);
if (mIpReachabilityDisconnectEnabled) {
handleIpReachabilityLost();
transitionTo(mDisconnectingState);
} else {
logd("CMD_IP_REACHABILITY_LOST but disconnect disabled -- ignore");
}
break;
case CMD_DISCONNECT:
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_GENERIC);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
if (message.arg1 == 1) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
mWifiNative.disconnect(mInterfaceName);
mTemporarilyDisconnectWifi = true;
transitionTo(mDisconnectingState);
}
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
mWifiInfo.setBSSID((String) message.obj);
mLastNetworkId = message.arg1;
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
if (!mLastBssid.equals(message.obj)) {
mLastBssid = (String) message.obj;
sendNetworkStateChangeBroadcast(mLastBssid);
}
break;
case CMD_ONESHOT_RSSI_POLL:
if (!mEnableRssiPolling) {
updateLinkLayerStatsRssiAndScoreReportInternal();
}
break;
case CMD_RSSI_POLL:
if (message.arg1 == mRssiPollToken) {
WifiLinkLayerStats stats = updateLinkLayerStatsRssiAndScoreReportInternal();
mWifiMetrics.updateWifiUsabilityStatsEntries(mWifiInfo, stats);
if (mWifiScoreReport.shouldCheckIpLayer()) {
if (mIpClient != null) {
mIpClient.confirmConfiguration();
}
mWifiScoreReport.noteIpCheck();
}
int statusDataStall = mWifiDataStall.checkForDataStall(
mLastLinkLayerStats, stats, mWifiInfo);
if (mDataStallTriggerTimeMs == -1
&& statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) {
mDataStallTriggerTimeMs = mClock.getElapsedSinceBootMillis();
mLastStatusDataStall = statusDataStall;
}
if (mDataStallTriggerTimeMs != -1) {
long elapsedTime = mClock.getElapsedSinceBootMillis()
- mDataStallTriggerTimeMs;
if (elapsedTime >= DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS) {
mDataStallTriggerTimeMs = -1;
mWifiMetrics.addToWifiUsabilityStatsList(
WifiUsabilityStats.LABEL_BAD,
convertToUsabilityStatsTriggerType(mLastStatusDataStall),
-1);
mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN;
}
}
mWifiMetrics.incrementWifiLinkLayerUsageStats(stats);
mLastLinkLayerStats = stats;
mWifiScoreCard.noteSignalPoll(mWifiInfo);
mLinkProbeManager.updateConnectionStats(
mWifiInfo, mInterfaceName);
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
mPollRssiIntervalMsecs);
if (mVerboseLoggingEnabled) sendRssiChangeBroadcast(mWifiInfo.getRssi());
mWifiTrafficPoller.notifyOnDataActivity(mWifiInfo.txSuccess,
mWifiInfo.rxSuccess);
} else {
// Polling has completed
}
break;
case CMD_ENABLE_RSSI_POLL:
cleanWifiScore();
mEnableRssiPolling = (message.arg1 == 1);
mRssiPollToken++;
if (mEnableRssiPolling) {
// First poll
mLastSignalLevel = -1;
mLinkProbeManager.resetOnScreenTurnedOn();
fetchRssiLinkSpeedAndFrequencyNative();
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
mPollRssiIntervalMsecs);
}
break;
case CMD_PKT_CNT_FETCH:
callbackIdentifier = message.arg2;
WifiNative.TxPacketCounters counters =
mWifiNative.getTxPacketCounters(mInterfaceName);
if (counters != null) {
sendTxPacketCountListenerSuccess(
callbackIdentifier, counters.txSucceeded + counters.txFailed);
} else {
sendTxPacketCountListenerSuccess(callbackIdentifier, WifiManager.ERROR);
}
break;
case WifiMonitor.ASSOCIATED_BSSID_EVENT:
if ((String) message.obj == null) {
logw("Associated command w/o BSSID");
break;
}
mLastBssid = (String) message.obj;
if (mLastBssid != null && (mWifiInfo.getBSSID() == null
|| !mLastBssid.equals(mWifiInfo.getBSSID()))) {
mWifiInfo.setBSSID(mLastBssid);
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
ScanDetailCache scanDetailCache = mWifiConfigManager
.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache != null) {
ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
if (scanResult != null) {
mWifiInfo.setFrequency(scanResult.frequency);
}
}
}
sendNetworkStateChangeBroadcast(mLastBssid);
}
break;
case CMD_START_RSSI_MONITORING_OFFLOAD:
case CMD_RSSI_THRESHOLD_BREACHED:
byte currRssi = (byte) message.arg1;
processRssiThreshold(currRssi, message.what, mRssiEventHandler);
break;
case CMD_STOP_RSSI_MONITORING_OFFLOAD:
stopRssiMonitoringOffload();
break;
case CMD_RECONNECT:
log(" Ignore CMD_RECONNECT request because wifi is already connected");
break;
case CMD_RESET_SIM_NETWORKS:
if (message.arg1 == 0 // sim was removed
&& mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
WifiConfiguration config =
mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
if (config.enterpriseConfig != null
&& config.enterpriseConfig.requireSimCredential()
&& !mTelephonyUtil.isSimPresent(mLastSubId)) {
// check if the removed sim card is associated with current config
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_RESET_SIM_NETWORKS);
mWifiNative.disconnect(mInterfaceName);
mWifiNative.removeNetworkCachedData(mLastNetworkId);
transitionTo(mDisconnectingState);
}
}
/* allow parent state to reset data for other networks */
handleStatus = NOT_HANDLED;
break;
case CMD_START_IP_PACKET_OFFLOAD: {
int slot = message.arg1;
int intervalSeconds = message.arg2;
KeepalivePacketData pkt = (KeepalivePacketData) message.obj;
int result = startWifiIPPacketOffload(slot, pkt, intervalSeconds);
if (mNetworkAgent != null) {
mNetworkAgent.onSocketKeepaliveEvent(slot, result);
}
break;
}
case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
if (mIpClient != null) {
final int slot = message.arg1;
if (message.obj instanceof NattKeepalivePacketData) {
final NattKeepalivePacketData pkt =
(NattKeepalivePacketData) message.obj;
mIpClient.addKeepalivePacketFilter(slot, pkt);
} else if (message.obj instanceof TcpKeepalivePacketData) {
final TcpKeepalivePacketData pkt =
(TcpKeepalivePacketData) message.obj;
mIpClient.addKeepalivePacketFilter(slot, pkt);
}
}
break;
}
case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
if (mIpClient != null) {
mIpClient.removeKeepalivePacketFilter(message.arg1);
}
break;
}
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
/**
* Fetches link stats and updates Wifi Score Report.
*/
private WifiLinkLayerStats updateLinkLayerStatsRssiAndScoreReportInternal() {
WifiLinkLayerStats stats = getWifiLinkLayerStats();
// Get Info and continue polling
fetchRssiLinkSpeedAndFrequencyNative();
// Send the update score to network agent.
mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mWifiMetrics);
return stats;
}
}
/**
* Fetches link stats and updates Wifi Score Report.
*/
public void updateLinkLayerStatsRssiAndScoreReport() {
sendMessage(CMD_ONESHOT_RSSI_POLL);
}
private static int convertToUsabilityStatsTriggerType(int unusableEventTriggerType) {
int triggerType;
switch (unusableEventTriggerType) {
case WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX:
triggerType = WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX;
break;
case WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX:
triggerType = WifiUsabilityStats.TYPE_DATA_STALL_TX_WITHOUT_RX;
break;
case WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH:
triggerType = WifiUsabilityStats.TYPE_DATA_STALL_BOTH;
break;
case WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT:
triggerType = WifiUsabilityStats.TYPE_FIRMWARE_ALERT;
break;
case WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST:
triggerType = WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST;
break;
default:
triggerType = WifiUsabilityStats.TYPE_UNKNOWN;
Log.e(TAG, "Unknown WifiIsUnusableEvent: " + unusableEventTriggerType);
}
return triggerType;
}
class ObtainingIpState extends State {
@Override
public void enter() {
final WifiConfiguration currentConfig = getCurrentWifiConfiguration();
final boolean isUsingStaticIp =
(currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
if (mVerboseLoggingEnabled) {
final String key = currentConfig.getKey();
log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
+ " " + key + " "
+ " roam=" + mIsAutoRoaming
+ " static=" + isUsingStaticIp);
}
// Send event to CM & network change broadcast
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to fail and the device to disconnect.
clearTargetBssid("ObtainingIpAddress");
// Stop IpClient in case we're switching from DHCP to static
// configuration or vice versa.
//
// When we transition from static configuration to DHCP in
// particular, we must tell ConnectivityService that we're
// disconnected, because DHCP might take a long time during which
// connectivity APIs such as getActiveNetworkInfo should not return
// CONNECTED.
stopIpClient();
if (mIpClient != null) {
mIpClient.setHttpProxy(currentConfig.getHttpProxy());
if (!TextUtils.isEmpty(mContext.getResources().getString(
R.string.config_wifi_tcp_buffers))) {
mIpClient.setTcpBufferSizes(mContext.getResources().getString(
R.string.config_wifi_tcp_buffers));
}
}
final ProvisioningConfiguration prov;
if (!isUsingStaticIp) {
prov = new ProvisioningConfiguration.Builder()
.withPreDhcpAction()
.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
.withNetwork(getCurrentNetwork())
.withDisplayName(currentConfig.SSID)
.build();
} else {
StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
prov = new ProvisioningConfiguration.Builder()
.withStaticConfiguration(staticIpConfig)
.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
.withNetwork(getCurrentNetwork())
.withDisplayName(currentConfig.SSID)
.build();
}
if (mIpClient != null) {
mIpClient.startProvisioning(prov);
}
// Get Link layer stats so as we get fresh tx packet counters
getWifiLinkLayerStats();
}
@Override
public boolean processMessage(Message message) {
boolean handleStatus = HANDLED;
switch(message.what) {
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(),
(message.obj == null)
? mTargetRoamBSSID : (String) message.obj,
WifiLastResortWatchdog.FAILURE_CODE_DHCP);
handleStatus = NOT_HANDLED;
break;
case CMD_SET_HIGH_PERF_MODE:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
}
/**
* Helper function to check if we need to invoke
* {@link NetworkAgent#explicitlySelected(boolean, boolean)} to indicate that we connected to a
* network which the user just chose
* (i.e less than {@link #LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS) before).
*/
@VisibleForTesting
public boolean shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration currentConfig) {
if (currentConfig == null) {
Log.wtf(TAG, "Current WifiConfiguration is null, but IP provisioning just succeeded");
return false;
}
long currentTimeMillis = mClock.getElapsedSinceBootMillis();
return (mWifiConfigManager.getLastSelectedNetwork() == currentConfig.networkId
&& currentTimeMillis - mWifiConfigManager.getLastSelectedTimeStamp()
< LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS);
}
private void sendConnectedState() {
// If this network was explicitly selected by the user, evaluate whether to inform
// ConnectivityService of that fact so the system can treat it appropriately.
WifiConfiguration config = getCurrentWifiConfiguration();
boolean explicitlySelected = false;
if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
// If explicitlySelected is true, the network was selected by the user via Settings or
// QuickSettings. If this network has Internet access, switch to it. Otherwise, switch
// to it only if the user confirms that they really want to switch, or has already
// confirmed and selected "Don't ask again".
explicitlySelected =
mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
if (mVerboseLoggingEnabled) {
log("Network selected by UID " + config.lastConnectUid + " explicitlySelected="
+ explicitlySelected);
}
}
if (mVerboseLoggingEnabled) {
log("explictlySelected=" + explicitlySelected + " acceptUnvalidated="
+ config.noInternetAccessExpected);
}
if (mNetworkAgent != null) {
mNetworkAgent.explicitlySelected(explicitlySelected, config.noInternetAccessExpected);
}
setNetworkDetailedState(DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
}
class RoamingState extends State {
boolean mAssociated;
@Override
public void enter() {
if (mVerboseLoggingEnabled) {
log("RoamingState Enter mScreenOn=" + mScreenOn);
}
// Make sure we disconnect if roaming fails
mRoamWatchdogCount++;
logd("Start Roam Watchdog " + mRoamWatchdogCount);
sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
mRoamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
mAssociated = false;
}
@Override
public boolean processMessage(Message message) {
WifiConfiguration config;
boolean handleStatus = HANDLED;
switch (message.what) {
case CMD_IP_CONFIGURATION_LOST:
config = getCurrentWifiConfiguration();
if (config != null) {
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_AUTOROAM_FAILURE);
}
handleStatus = NOT_HANDLED;
break;
case CMD_UNWANTED_NETWORK:
if (mVerboseLoggingEnabled) {
log("Roaming and CS doesn't want the network -> ignore");
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
/**
* If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
* before NETWORK_DISCONNECTION_EVENT
* And there is an associated BSSID corresponding to our target BSSID, then
* we have missed the network disconnection, transition to mDisconnectedState
* and handle the rest of the events there.
*/
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
if (stateChangeResult.state == SupplicantState.DISCONNECTED
|| stateChangeResult.state == SupplicantState.INACTIVE
|| stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
if (mVerboseLoggingEnabled) {
log("STATE_CHANGE_EVENT in roaming state "
+ stateChangeResult.toString());
}
if (stateChangeResult.BSSID != null
&& stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
}
if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
// We completed the layer2 roaming part
mAssociated = true;
if (stateChangeResult.BSSID != null) {
mTargetRoamBSSID = stateChangeResult.BSSID;
}
}
break;
case CMD_ROAM_WATCHDOG_TIMER:
if (mRoamWatchdogCount == message.arg1) {
if (mVerboseLoggingEnabled) log("roaming watchdog! -> disconnect");
mWifiMetrics.endConnectionEvent(
WifiMetrics.ConnectionEvent.FAILURE_ROAM_TIMEOUT,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mRoamFailCount++;
handleNetworkDisconnect();
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectedState);
}
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (mAssociated) {
if (mVerboseLoggingEnabled) {
log("roaming and Network connection established");
}
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
int reasonCode = message.arg2;
sendNetworkStateChangeBroadcast(mLastBssid);
// Successful framework roam! (probably)
mBssidBlocklistMonitor.handleBssidConnectionSuccess(mLastBssid,
mWifiInfo.getSSID());
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NONE,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified by QNS would cause
// the roam to fail and the device to disconnect.
// When transition from RoamingState to DisconnectingState or
// DisconnectedState, the config BSSID is cleared by
// handleNetworkDisconnect().
clearTargetBssid("RoamingCompleted");
// We used to transition to ObtainingIpState in an
// attempt to do DHCPv4 RENEWs on framework roams.
// DHCP can take too long to time out, and we now rely
// upon IpClient's use of IpReachabilityMonitor to
// confirm our current network configuration.
//
// mIpClient.confirmConfiguration() is called within
// the handling of SupplicantState.COMPLETED.
transitionTo(mConnectedState);
} else {
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
}
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
// Throw away but only if it corresponds to the network we're roaming to
String bssid = (String) message.obj;
if (true) {
String target = "";
if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
log("NETWORK_DISCONNECTION_EVENT in roaming state"
+ " BSSID=" + bssid
+ " target=" + target);
}
if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
@Override
public void exit() {
logd("ClientModeImpl: Leaving Roaming state");
}
}
class ConnectedState extends State {
@Override
public void enter() {
if (mVerboseLoggingEnabled) {
log("Enter ConnectedState mScreenOn=" + mScreenOn);
}
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NONE,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_CONNECTED);
registerConnected();
mLastConnectAttemptTimestamp = 0;
mTargetWifiConfiguration = null;
mWifiScoreReport.reset();
mLastSignalLevel = -1;
// Not roaming anymore
mIsAutoRoaming = false;
mLastDriverRoamAttempt = 0;
mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(true);
mWifiStateTracker.updateState(WifiStateTracker.CONNECTED);
//Inform WifiLockManager
WifiLockManager wifiLockManager = mWifiInjector.getWifiLockManager();
wifiLockManager.updateWifiClientConnected(true);
}
@Override
public boolean processMessage(Message message) {
WifiConfiguration config = null;
boolean handleStatus = HANDLED;
switch (message.what) {
case CMD_UNWANTED_NETWORK:
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_UNWANTED);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
} else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
|| message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) {
Log.d(TAG, (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
? "NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN"
: "NETWORK_STATUS_UNWANTED_VALIDATION_FAILED"));
config = getCurrentWifiConfiguration();
if (config != null) {
// Disable autojoin
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, false);
mWifiConfigManager.updateNetworkSelectionStatus(config.networkId,
DISABLED_NO_INTERNET_PERMANENT);
} else {
// stop collect last-mile stats since validation fail
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(
WifiDiagnostics.CONNECTION_EVENT_FAILED);
mWifiConfigManager.incrementNetworkNoInternetAccessReports(
config.networkId);
// If this was not the last selected network, update network
// selection status to temporarily disable the network.
if (mWifiConfigManager.getLastSelectedNetwork() != config.networkId
&& !config.noInternetAccessExpected) {
Log.i(TAG, "Temporarily disabling network because of"
+ "no-internet access");
mWifiConfigManager.updateNetworkSelectionStatus(
config.networkId,
DISABLED_NO_INTERNET_TEMPORARY);
}
mBssidBlocklistMonitor.handleBssidConnectionFailure(
mLastBssid, config.SSID,
BssidBlocklistMonitor
.REASON_NETWORK_VALIDATION_FAILURE);
}
}
}
break;
case CMD_NETWORK_STATUS:
if (message.arg1 == NetworkAgent.VALID_NETWORK) {
// stop collect last-mile stats since validation pass
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(
WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
mWifiScoreCard.noteValidationSuccess(mWifiInfo);
mBssidBlocklistMonitor.handleNetworkValidationSuccess(mLastBssid,
mWifiInfo.getSSID());
config = getCurrentWifiConfiguration();
if (config != null) {
// re-enable autojoin
mWifiConfigManager.updateNetworkSelectionStatus(
config.networkId,
WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_ENABLE);
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, true);
}
}
break;
case CMD_ACCEPT_UNVALIDATED:
boolean accept = (message.arg1 != 0);
mWifiConfigManager.setNetworkNoInternetAccessExpected(mLastNetworkId, accept);
break;
case WifiMonitor.ASSOCIATED_BSSID_EVENT:
// ASSOCIATING to a new BSSID while already connected, indicates
// that driver is roaming
mLastDriverRoamAttempt = mClock.getWallClockMillis();
handleStatus = NOT_HANDLED;
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
long lastRoam = 0;
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
if (mLastDriverRoamAttempt != 0) {
// Calculate time since last driver roam attempt
lastRoam = mClock.getWallClockMillis() - mLastDriverRoamAttempt;
mLastDriverRoamAttempt = 0;
}
if (unexpectedDisconnectedReason(message.arg2)) {
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_UNEXPECTED_DISCONNECT);
}
boolean localGen = message.arg1 == 1;
if (!localGen) { // ignore disconnects initiatied by wpa_supplicant.
mBssidBlocklistMonitor.handleBssidConnectionFailure(mWifiInfo.getBSSID(),
mWifiInfo.getSSID(),
BssidBlocklistMonitor.REASON_ABNORMAL_DISCONNECT);
}
config = getCurrentWifiConfiguration();
if (mVerboseLoggingEnabled) {
log("NETWORK_DISCONNECTION_EVENT in connected state"
+ " BSSID=" + mWifiInfo.getBSSID()
+ " RSSI=" + mWifiInfo.getRssi()
+ " freq=" + mWifiInfo.getFrequency()
+ " reason=" + message.arg2
+ " Network Selection Status=" + (config == null ? "Unavailable"
: config.getNetworkSelectionStatus().getNetworkStatusString()));
}
break;
case CMD_START_ROAM:
// Clear the driver roam indication since we are attempting a framework roam
mLastDriverRoamAttempt = 0;
/* Connect command coming from auto-join */
int netId = message.arg1;
ScanResult candidate = (ScanResult) message.obj;
String bssid = SUPPLICANT_BSSID_ANY;
if (candidate != null) {
bssid = candidate.BSSID;
}
config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
if (config == null) {
loge("CMD_START_ROAM and no config, bail out...");
break;
}
mWifiScoreCard.noteConnectionAttempt(mWifiInfo);
setTargetBssid(config, bssid);
mTargetNetworkId = netId;
logd("CMD_START_ROAM sup state "
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " config " + config.getKey()
+ " targetRoamBSSID " + mTargetRoamBSSID);
reportConnectionAttemptStart(config, mTargetRoamBSSID,
WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
if (mWifiNative.roamToNetwork(mInterfaceName, config)) {
mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
mTargetWifiConfiguration = config;
mIsAutoRoaming = true;
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_ROAM, config);
transitionTo(mRoamingState);
} else {
loge("CMD_START_ROAM Failed to start roaming to network " + config);
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
break;
}
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
@Override
public void exit() {
logd("ClientModeImpl: Leaving Connected state");
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
mLastDriverRoamAttempt = 0;
mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(false);
}
}
class DisconnectingState extends State {
@Override
public void enter() {
if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectingState State screenOn=" + mScreenOn);
}
// Make sure we disconnect: we enter this state prior to connecting to a new
// network, waiting for either a DISCONNECT event or a SUPPLICANT_STATE_CHANGE
// event which in this case will be indicating that supplicant started to associate.
// In some cases supplicant doesn't ignore the connect requests (it might not
// find the target SSID in its cache),
// Therefore we end up stuck that state, hence the need for the watchdog.
mDisconnectingWatchdogCount++;
logd("Start Disconnecting Watchdog " + mDisconnectingWatchdogCount);
sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
mDisconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
}
@Override
public boolean processMessage(Message message) {
boolean handleStatus = HANDLED;
switch (message.what) {
case CMD_CONNECT_NETWORK:
case CMD_SAVE_NETWORK:
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
case CMD_DISCONNECT:
if (mVerboseLoggingEnabled) {
log("Ignore CMD_DISCONNECT when already disconnecting.");
}
break;
case CMD_DISCONNECTING_WATCHDOG_TIMER:
if (mDisconnectingWatchdogCount == message.arg1) {
if (mVerboseLoggingEnabled) log("disconnecting watchdog! -> disconnect");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
/**
* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
* we have missed the network disconnection, transition to mDisconnectedState
* and handle the rest of the events there
*/
mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
}
class DisconnectedState extends State {
@Override
public void enter() {
Log.i(TAG, "disconnectedstate enter");
// We don't scan frequently if this is a temporary disconnect
// due to p2p
if (mTemporarilyDisconnectWifi) {
p2pSendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
return;
}
if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectedState screenOn=" + mScreenOn);
}
/** clear the roaming state, if we were roaming, we failed */
mIsAutoRoaming = false;
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
}
@Override
public boolean processMessage(Message message) {
boolean handleStatus = HANDLED;
switch (message.what) {
case CMD_DISCONNECT:
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_GENERIC);
mWifiNative.disconnect(mInterfaceName);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (message.arg2 == 15 /* FOURWAY_HANDSHAKE_TIMEOUT */) {
String bssid = (message.obj == null)
? mTargetRoamBSSID : (String) message.obj;
mWifiInjector.getWifiLastResortWatchdog()
.noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(), bssid,
WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
if (mVerboseLoggingEnabled) {
logd("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state
+ " -> state= "
+ WifiInfo.getDetailedStateOf(stateChangeResult.state));
}
if (SupplicantState.isConnecting(stateChangeResult.state)) {
WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(
stateChangeResult.networkId);
// Update Passpoint information before setNetworkDetailedState as
// WifiTracker monitors NETWORK_STATE_CHANGED_ACTION to update UI.
mWifiInfo.setFQDN(null);
mWifiInfo.setOsuAp(false);
mWifiInfo.setProviderFriendlyName(null);
if (config != null && (config.isPasspoint() || config.osu)) {
if (config.isPasspoint()) {
mWifiInfo.setFQDN(config.FQDN);
} else {
mWifiInfo.setOsuAp(true);
}
mWifiInfo.setProviderFriendlyName(config.providerFriendlyName);
}
}
setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
/* ConnectModeState does the rest of the handling */
handleStatus = NOT_HANDLED;
break;
case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
NetworkInfo info = (NetworkInfo) message.obj;
mP2pConnected.set(info.isConnected());
break;
case CMD_RECONNECT:
case CMD_REASSOCIATE:
if (mTemporarilyDisconnectWifi) {
// Drop a third party reconnect/reassociate if STA is
// temporarily disconnected for p2p
break;
} else {
// ConnectModeState handles it
handleStatus = NOT_HANDLED;
}
break;
case CMD_SCREEN_STATE_CHANGED:
handleScreenStateChanged(message.arg1 != 0);
break;
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
@Override
public void exit() {
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
}
}
/**
* State machine initiated requests can have replyTo set to null, indicating
* there are no recipients, we ignore those reply actions.
*/
private void replyToMessage(Message msg, int what) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, int arg1) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
dstMsg.arg1 = arg1;
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, Object obj) {
if (msg.replyTo == null) return;
Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
dstMsg.obj = obj;
mReplyChannel.replyToMessage(msg, dstMsg);
}
/**
* arg2 on the source message has a unique id that needs to be retained in replies
* to match the request
* <p>see WifiManager for details
*/
private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) {
Message msg = Message.obtain();
msg.what = what;
msg.arg2 = srcMsg.arg2;
return msg;
}
/**
* Notify interested parties if a wifi config has been changed.
*
* @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
* @param config Must have a WifiConfiguration object to succeed
* TODO: b/35258354 investigate if this can be removed. Is the broadcast sent by
* WifiConfigManager sufficient?
*/
private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
WifiConfiguration config) {
if (config != null && config.preSharedKey != null) {
Intent intent = new Intent(WifiManager.WIFI_CREDENTIAL_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID);
intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE,
wifiCredentialEventType);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE);
}
}
void handleGsmAuthRequest(SimAuthRequestData requestData) {
if (mTargetWifiConfiguration != null
&& mTargetWifiConfiguration.networkId
== requestData.networkId) {
logd("id matches targetWifiConfiguration");
} else {
logd("id does not match targetWifiConfiguration");
return;
}
/*
* Try authentication in the following order.
*
* Standard Cellular_auth Type Command
*
* 1. 3GPP TS 31.102 3G_authentication [Length][RAND][Length][AUTN]
* [Length][RES][Length][CK][Length][IK] and more
* 2. 3GPP TS 31.102 2G_authentication [Length][RAND]
* [Length][SRES][Length][Cipher Key Kc]
* 3. 3GPP TS 11.11 2G_authentication [RAND]
* [SRES][Cipher Key Kc]
*/
String response = mTelephonyUtil
.getGsmSimAuthResponse(requestData.data, mTargetWifiConfiguration);
if (response == null) {
// In case of failure, issue may be due to sim type, retry as No.2 case
response = mTelephonyUtil
.getGsmSimpleSimAuthResponse(requestData.data, mTargetWifiConfiguration);
if (response == null) {
// In case of failure, issue may be due to sim type, retry as No.3 case
response = mTelephonyUtil.getGsmSimpleSimNoLengthAuthResponse(
requestData.data, mTargetWifiConfiguration);
}
}
if (response == null || response.length() == 0) {
mWifiNative.simAuthFailedResponse(mInterfaceName);
} else {
logv("Supplicant Response -" + response);
mWifiNative.simAuthResponse(
mInterfaceName, WifiNative.SIM_AUTH_RESP_TYPE_GSM_AUTH, response);
}
}
void handle3GAuthRequest(SimAuthRequestData requestData) {
if (mTargetWifiConfiguration != null
&& mTargetWifiConfiguration.networkId
== requestData.networkId) {
logd("id matches targetWifiConfiguration");
} else {
logd("id does not match targetWifiConfiguration");
return;
}
SimAuthResponseData response = mTelephonyUtil
.get3GAuthResponse(requestData, mTargetWifiConfiguration);
if (response != null) {
mWifiNative.simAuthResponse(
mInterfaceName, response.type, response.response);
} else {
mWifiNative.umtsAuthFailedResponse(mInterfaceName);
}
}
/**
* Automatically connect to the network specified
*
* @param networkId ID of the network to connect to
* @param uid UID of the app triggering the connection.
* @param bssid BSSID of the network
*/
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
/**
* Automatically roam to the network specified
*
* @param networkId ID of the network to roam to
* @param scanResult scan result which identifies the network to roam to
*/
public void startRoamToNetwork(int networkId, ScanResult scanResult) {
sendMessage(CMD_START_ROAM, networkId, 0, scanResult);
}
/**
* Dynamically turn on/off WifiConnectivityManager
*
* @param enabled true-enable; false-disable
*/
public void enableWifiConnectivityManager(boolean enabled) {
sendMessage(CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER, enabled ? 1 : 0);
}
/**
* @param reason reason code from supplicant on network disconnected event
* @return true if this is a suspicious disconnect
*/
static boolean unexpectedDisconnectedReason(int reason) {
return reason == 2 // PREV_AUTH_NOT_VALID
|| reason == 6 // CLASS2_FRAME_FROM_NONAUTH_STA
|| reason == 7 // FRAME_FROM_NONASSOC_STA
|| reason == 8 // STA_HAS_LEFT
|| reason == 9 // STA_REQ_ASSOC_WITHOUT_AUTH
|| reason == 14 // MICHAEL_MIC_FAILURE
|| reason == 15 // 4WAY_HANDSHAKE_TIMEOUT
|| reason == 16 // GROUP_KEY_UPDATE_TIMEOUT
|| reason == 18 // GROUP_CIPHER_NOT_VALID
|| reason == 19 // PAIRWISE_CIPHER_NOT_VALID
|| reason == 23 // IEEE_802_1X_AUTH_FAILED
|| reason == 34; // DISASSOC_LOW_ACK
}
private static String getLinkPropertiesSummary(LinkProperties lp) {
List<String> attributes = new ArrayList<>(6);
if (lp.hasIpv4Address()) {
attributes.add("v4");
}
if (lp.hasIPv4DefaultRoute()) {
attributes.add("v4r");
}
if (lp.hasIPv4DnsServer()) {
attributes.add("v4dns");
}
if (lp.hasGlobalIpv6Address()) {
attributes.add("v6");
}
if (lp.hasIpv6DefaultRoute()) {
attributes.add("v6r");
}
if (lp.hasIPv6DnsServer()) {
attributes.add("v6dns");
}
return TextUtils.join(" ", attributes);
}
/**
* Gets the SSID from the WifiConfiguration pointed at by 'mTargetNetworkId'
* This should match the network config framework is attempting to connect to.
*/
private String getTargetSsid() {
WifiConfiguration currentConfig = mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (currentConfig != null) {
return currentConfig.SSID;
}
return null;
}
/**
* Send message to WifiP2pServiceImpl.
* @return true if message is sent.
* false if there is no channel configured for WifiP2pServiceImpl.
*/
private boolean p2pSendMessage(int what) {
if (mWifiP2pChannel != null) {
mWifiP2pChannel.sendMessage(what);
return true;
}
return false;
}
/**
* Send message to WifiP2pServiceImpl with an additional param |arg1|.
* @return true if message is sent.
* false if there is no channel configured for WifiP2pServiceImpl.
*/
private boolean p2pSendMessage(int what, int arg1) {
if (mWifiP2pChannel != null) {
mWifiP2pChannel.sendMessage(what, arg1);
return true;
}
return false;
}
/**
* Check if there is any connection request for WiFi network.
*/
private boolean hasConnectionRequests() {
return mNetworkFactory.hasConnectionRequests()
|| mUntrustedNetworkFactory.hasConnectionRequests();
}
/**
* Returns whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.
*/
public boolean getIpReachabilityDisconnectEnabled() {
return mIpReachabilityDisconnectEnabled;
}
/**
* Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.
*/
public void setIpReachabilityDisconnectEnabled(boolean enabled) {
mIpReachabilityDisconnectEnabled = enabled;
}
/**
* Sends a message to initialize the ClientModeImpl.
*
* @return true if succeeded, false otherwise.
*/
public boolean syncInitialize(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_INITIALIZE);
boolean result = (resultMsg.arg1 != FAILURE);
resultMsg.recycle();
return result;
}
/**
* Add a network request match callback to {@link WifiNetworkFactory}.
*/
public void addNetworkRequestMatchCallback(IBinder binder,
INetworkRequestMatchCallback callback,
int callbackIdentifier) {
mNetworkFactory.addCallback(binder, callback, callbackIdentifier);
}
/**
* Remove a network request match callback from {@link WifiNetworkFactory}.
*/
public void removeNetworkRequestMatchCallback(int callbackIdentifier) {
mNetworkFactory.removeCallback(callbackIdentifier);
}
/**
* Remove all approved access points from {@link WifiNetworkFactory} for the provided package.
*/
public void removeNetworkRequestUserApprovedAccessPointsForApp(@NonNull String packageName) {
mNetworkFactory.removeUserApprovedAccessPointsForApp(packageName);
}
/**
* Clear all approved access points from {@link WifiNetworkFactory}.
*/
public void clearNetworkRequestUserApprovedAccessPoints() {
mNetworkFactory.clear();
}
/**
* Gets the factory MAC address of wlan0 (station interface).
* @return String representation of the factory MAC address.
*/
public String getFactoryMacAddress() {
MacAddress macAddress = mWifiNative.getFactoryMacAddress(mInterfaceName);
if (macAddress != null) {
return macAddress.toString();
}
if (!isConnectedMacRandomizationEnabled()) {
return mWifiNative.getMacAddress(mInterfaceName);
}
return null;
}
/**
* Sets the current device mobility state.
* @param state the new device mobility state
*/
public void setDeviceMobilityState(@DeviceMobilityState int state) {
mWifiConnectivityManager.setDeviceMobilityState(state);
}
/**
* Updates the Wi-Fi usability score.
* @param seqNum Sequence number of the Wi-Fi usability score.
* @param score The Wi-Fi usability score.
* @param predictionHorizonSec Prediction horizon of the Wi-Fi usability score.
*/
public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
mWifiMetrics.incrementWifiUsabilityScoreCount(seqNum, score, predictionHorizonSec);
}
/**
* Sends a link probe.
*/
@VisibleForTesting
public void probeLink(WifiNative.SendMgmtFrameCallback callback, int mcs) {
mWifiNative.probeLink(mInterfaceName, MacAddress.fromString(mWifiInfo.getBSSID()),
callback, mcs);
}
private void sendActionListenerFailure(int callbackIdentifier, int reason) {
IActionListener actionListener;
synchronized (mProcessingActionListeners) {
actionListener = mProcessingActionListeners.remove(callbackIdentifier);
}
if (actionListener != null) {
try {
actionListener.onFailure(reason);
} catch (RemoteException e) {
// no-op (client may be dead, nothing to be done)
}
}
}
private void sendActionListenerSuccess(int callbackIdentifier) {
IActionListener actionListener;
synchronized (mProcessingTxPacketCountListeners) {
actionListener = mProcessingActionListeners.remove(callbackIdentifier);
}
if (actionListener != null) {
try {
actionListener.onSuccess();
} catch (RemoteException e) {
// no-op (client may be dead, nothing to be done)
}
}
}
private void sendTxPacketCountListenerFailure(int callbackIdentifier, int reason) {
ITxPacketCountListener txPacketCountListener;
synchronized (mProcessingTxPacketCountListeners) {
txPacketCountListener = mProcessingTxPacketCountListeners.remove(callbackIdentifier);
}
if (txPacketCountListener != null) {
try {
txPacketCountListener.onFailure(reason);
} catch (RemoteException e) {
// no-op (client may be dead, nothing to be done)
}
}
}
private void sendTxPacketCountListenerSuccess(int callbackIdentifier, int count) {
ITxPacketCountListener txPacketCountListener;
synchronized (mProcessingActionListeners) {
txPacketCountListener = mProcessingTxPacketCountListeners.remove(callbackIdentifier);
}
if (txPacketCountListener != null) {
try {
txPacketCountListener.onSuccess(count);
} catch (RemoteException e) {
// no-op (client may be dead, nothing to be done)
}
}
}
/**
* Trigger network connection and provide status via the provided callback.
*/
public void connect(WifiConfiguration config, int netId, @Nullable IBinder binder,
@Nullable IActionListener callback, int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
if (callback != null && binder != null) {
mProcessingActionListeners.add(binder, callback, callbackIdentifier);
}
/**
* The connect message can contain a network id passed as arg1 on message or
* or a config passed as obj on message.
* For a new network, a config is passed to create and connect.
* For an existing network, a network id is passed
*/
NetworkUpdateResult result = null;
if (config != null) {
result = mWifiConfigManager.addOrUpdateNetwork(config, callingUid);
if (!result.isSuccess()) {
loge("connectNetwork adding/updating config=" + config + " failed");
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
} else {
if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
loge("connectNetwork Invalid network Id=" + netId);
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
result = new NetworkUpdateResult(netId);
}
final int networkId = result.getNetworkId();
if (!mWifiConfigManager.enableNetwork(networkId, true, callingUid, null)
|| !mWifiConfigManager.updateLastConnectUid(networkId, callingUid)) {
logi("connect Allowing uid " + callingUid
+ " with insufficient permissions to connect=" + networkId);
} else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) {
// Note user connect choice here, so that it will be considered in the
// next network selection.
mWifiConnectivityManager.setUserConnectChoice(networkId);
}
Message message =
obtainMessage(CMD_CONNECT_NETWORK, -1, callbackIdentifier, result);
message.sendingUid = callingUid;
sendMessage(message);
});
}
/**
* Trigger network save and provide status via the provided callback.
*/
public void save(WifiConfiguration config, @Nullable IBinder binder,
@Nullable IActionListener callback, int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
if (callback != null && binder != null) {
mProcessingActionListeners.add(binder, callback, callbackIdentifier);
}
if (config == null) {
loge("saveNetwork with null configuration my state "
+ getCurrentState().getName());
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(config, callingUid);
if (!result.isSuccess()) {
loge("saveNetwork adding/updating config=" + config + " failed");
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
if (!mWifiConfigManager.enableNetwork(
result.getNetworkId(), false, callingUid, null)) {
loge("saveNetwork enabling config=" + config + " failed");
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
Message message =
obtainMessage(CMD_SAVE_NETWORK, -1 , callbackIdentifier, result);
message.sendingUid = callingUid;
sendMessage(message);
});
}
/**
* Trigger network forget and provide status via the provided callback.
*/
public void forget(int netId, @Nullable IBinder binder, @Nullable IActionListener callback,
int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
if (callback != null && binder != null) {
mProcessingActionListeners.add(binder, callback, callbackIdentifier);
}
boolean success = mWifiConfigManager.removeNetwork(netId, callingUid, null);
if (!success) {
loge("Failed to remove network");
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
}
sendActionListenerSuccess(callbackIdentifier);
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT, null);
});
}
/**
* Retrieve tx packet count and provide status via the provided callback.
*/
public void getTxPacketCount(IBinder binder, @NonNull ITxPacketCountListener callback,
int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
mProcessingTxPacketCountListeners.add(binder, callback, callbackIdentifier);
Message message = obtainMessage(CMD_PKT_CNT_FETCH, -1, callbackIdentifier, null);
message.sendingUid = callingUid;
sendMessage(message);
});
}
}