/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; 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.ApplicationInfo; 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.NetworkUtils; 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.ProvisioningConfiguration; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.RssiPacketCountInfo; 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.WifiSsid; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; 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.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; 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.nano.WifiMetricsProto; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent; import com.android.server.wifi.nano.WifiMetricsProto.WifiUsabilityStats; import com.android.server.wifi.p2p.WifiP2pServiceImpl; import com.android.server.wifi.util.NativeUtil; 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 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.HashMap; import java.util.List; import java.util.Map; 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; 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 = 10_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 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 McastLockManagerFilterController mMcastLockManagerFilterController; private boolean mScreenOn = false; private String mInterfaceName; private int mLastSignalLevel = -1; private String mLastBssid; private int mLastNetworkId; // The network Id we successfully joined 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 ClientModeImpl 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; // Wakelock held during wifi start/stop and driver load/unload private PowerManager.WakeLock mWakeLock; private Context mContext; private final Object mDhcpResultsLock = new Object(); private DhcpResults mDhcpResults; // 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.configKey()); } 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.configKey()); } 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(); /* The base for wifi message types */ static final int BASE = Protocol.BASE_WIFI; static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31; /* Supplicant commands */ /* Add/update a network configuration */ static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52; /* Delete a network */ static final int CMD_REMOVE_NETWORK = BASE + 53; /* Enable a network. The device will attempt a connection to the given network. */ static final int CMD_ENABLE_NETWORK = BASE + 54; /* Get configured networks */ static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59; /* Get adaptors */ static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61; /* Get configured networks with real preSharedKey */ static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62; /* Get Link Layer Stats thru HAL */ static final int CMD_GET_LINK_LAYER_STATS = BASE + 63; /* Supplicant commands after driver start*/ /* 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; /* Remove a packages associated configurations */ static final int CMD_REMOVE_APP_CONFIGURATIONS = BASE + 97; /* Disable an ephemeral network */ static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98; /* 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; /* try to match a provider with current network */ static final int CMD_MATCH_PROVIDER_NETWORK = BASE + 105; // Add or update a Passpoint configuration. static final int CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG = BASE + 106; // Remove a Passpoint configuration. static final int CMD_REMOVE_PASSPOINT_CONFIG = BASE + 107; // Get the list of installed Passpoint configurations. static final int CMD_GET_PASSPOINT_CONFIGS = BASE + 108; // Get the list of OSU providers associated with a Passpoint network. static final int CMD_GET_MATCHING_OSU_PROVIDERS = BASE + 109; // Get the list of installed Passpoint configurations matched with OSU providers static final int CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS = BASE + 110; /* Commands from/to the SupplicantStateTracker */ /* Reset the supplicant state tracker */ static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111; // Get the list of wifi configurations for installed Passpoint profiles static final int CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES = BASE + 112; 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; /* Supplicant is trying to associate to a given BSSID */ static final int CMD_TARGET_BSSID = BASE + 141; 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_ASSOCIATED_BSSID = BASE + 147; 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; /* Remove a packages associated configrations */ static final int CMD_REMOVE_USER_CONFIGURATIONS = BASE + 152; 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; /* Get FQDN list for Passpoint profiles matched with a given scanResults */ static final int CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS = BASE + 168; /** * 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; /* used to indicate that the foreground user was switched */ static final int CMD_USER_SWITCH = BASE + 205; /* used to indicate that the foreground user was switched */ static final int CMD_USER_UNLOCK = BASE + 206; /* used to indicate that the foreground user was switched */ static final int CMD_USER_STOP = BASE + 207; /* 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; // For message logging. private static final Class[] sMessageClasses = { AsyncChannel.class, ClientModeImpl.class }; private static final SparseArray 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); /** * Keep track of whether WIFI is running. */ private boolean mIsRunning = false; /** * Keep track of whether we last told the battery stats we had started. */ private boolean mReportedRunning = false; /** * Most recently set source of starting WIFI. */ private final WorkSource mRunningWifiUids = new WorkSource(); /** * The last reported UIDs that were responsible for starting WIFI. */ private final WorkSource mLastRunningWifiUids = new WorkSource(); private TelephonyManager mTelephonyManager; private TelephonyManager getTelephonyManager() { if (mTelephonyManager == null) { mTelephonyManager = mWifiInjector.makeTelephonyManager(); } return mTelephonyManager; } private final IBatteryStats mBatteryStats; private final String mTcpBufferSizes; // 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; private boolean mConnectedMacRandomzationSupported; 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) { 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; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); mBatteryStats = IBatteryStats.Stub.asInterface(mFacade.getService( BatteryStats.SERVICE_NAME)); mWifiStateTracker = wifiInjector.getWifiStateTracker(); IBinder b = mFacade.getService(Context.NETWORKMANAGEMENT_SERVICE); 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(); mSupplicantStateTracker = mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler()); mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this); mLinkProperties = new LinkProperties(); mMcastLockManagerFilterController = new McastLockManagerFilterController(); mNetworkInfo.setIsAvailable(false); mLastBssid = null; mLastNetworkId = WifiConfiguration.INVALID_NETWORK_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(); 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); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName()); mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend"); mSuspendWakeLock.setReferenceCounted(false); mConnectedMacRandomzationSupported = mContext.getResources() .getBoolean(R.bool.config_wifi_connected_mac_randomization_supported); mWifiInfo.setEnableConnectedMacRandomization(mConnectedMacRandomzationSupported); mWifiMetrics.setIsMacRandomizationOn(mConnectedMacRandomzationSupported); mTcpBufferSizes = mContext.getResources().getString( R.string.config_wifi_tcp_buffers); // 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, CMD_TARGET_BSSID, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, CMD_ASSOCIATED_BSSID, 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, CMD_ASSOCIATED_BSSID, mWifiMetrics.getHandler()); mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID, mWifiMetrics.getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT, mWifiInjector.getWifiLastResortWatchdog().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(); } } /** * 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(ActivityManager.isLowRamDeviceStatic() ? 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.startLogging(mVerboseLoggingEnabled); mWifiMonitor.enableVerboseLogging(verbose); mWifiNative.enableVerboseLogging(verbose); mWifiConfigManager.enableVerboseLogging(verbose); mSupplicantStateTracker.enableVerboseLogging(verbose); mPasspointManager.enableVerboseLogging(verbose); mNetworkFactory.enableVerboseLogging(verbose); mLinkProbeManager.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 boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) { logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid + ", forceReconnect = " + forceReconnect); WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId); if (config == null) { loge("connectToUserSelectNetwork Invalid network Id=" + netId); return false; } if (!mWifiConfigManager.enableNetwork(netId, true, uid) || !mWifiConfigManager.updateLastConnectUid(netId, uid)) { logi("connectToUserSelectNetwork Allowing uid " + uid + " with insufficient permissions to connect=" + netId); } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { // Note user connect choice here, so that it will be considered in the next network // selection. mWifiConnectivityManager.setUserConnectChoice(netId); } 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(config.networkId, WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL); } startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY); } return true; } /** * ****************************************************** * 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; } /** * Blocking method to match the provider with the current network * * @param channel AsyncChannel to use for the response * @param fqdn * @return int returns message result */ public int matchProviderWithCurrentNetwork(AsyncChannel channel, String fqdn) { Message resultMsg = channel.sendMessageSynchronously(CMD_MATCH_PROVIDER_NETWORK, fqdn); int result = resultMsg.arg1; resultMsg.recycle(); return result; } /** * 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 } /** * Method to disable an ephemeral config for an ssid * * @param ssid network name to disable */ public void disableEphemeralNetwork(String ssid) { if (ssid != null) { sendMessage(CMD_DISABLE_EPHEMERAL_NETWORK, ssid); } } /** * 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); /** * Add a network synchronously * * @return network id of the new network */ public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) { Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config); if (messageIsNull(resultMsg)) return WifiConfiguration.INVALID_NETWORK_ID; int result = resultMsg.arg1; resultMsg.recycle(); return result; } /** * Get configured networks synchronously * * @param channel * @return */ public List syncGetConfiguredNetworks(int uuid, AsyncChannel channel, int targetUid) { Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid, targetUid); if (messageIsNull(resultMsg)) return null; List result = (List) resultMsg.obj; resultMsg.recycle(); return result; } /** * Blocking call to get the current WifiConfiguration by a privileged caller so private data, * like the password, is not redacted. * * @param channel AsyncChannel to use for the response * @return List list of configured networks configs */ public List syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously( CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS); if (messageIsNull(resultMsg)) return null; List result = (List) resultMsg.obj; resultMsg.recycle(); return result; } /** * Returns the list of FQDN (Fully Qualified Domain Name) to installed Passpoint configurations. * * Return the map of all matching configurations with corresponding scanResults (or an empty map * if none). * * @param scanResults The list of scan results * @return Map that consists of FQDN (Fully Qualified Domain Name) and corresponding * scanResults per network type({@link WifiManager#PASSPOINT_HOME_NETWORK} and {@link * WifiManager#PASSPOINT_ROAMING_NETWORK}). */ @NonNull Map>> syncGetAllMatchingFqdnsForScanResults( List scanResults, AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously( CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS, scanResults); if (messageIsNull(resultMsg)) return new HashMap<>(); Map>> configs = (Map>>) resultMsg.obj; resultMsg.recycle(); return configs; } /** * Retrieve a list of {@link OsuProvider} associated with the given list of ScanResult * synchronously. * * @param scanResults a list of ScanResult that has Passpoint APs. * @param channel Channel for communicating with the state machine * @return Map that consists of {@link OsuProvider} and a matching list of {@link ScanResult}. */ @NonNull public Map> syncGetMatchingOsuProviders( List scanResults, AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously(CMD_GET_MATCHING_OSU_PROVIDERS, scanResults); if (messageIsNull(resultMsg)) return new HashMap<>(); Map> providers = (Map>) resultMsg.obj; resultMsg.recycle(); return providers; } /** * Returns the matching Passpoint configurations for given OSU(Online Sign-Up) Providers * * @param osuProviders a list of {@link OsuProvider} * @param channel AsyncChannel to use for the response * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}. */ @NonNull public Map syncGetMatchingPasspointConfigsForOsuProviders( List osuProviders, AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously( CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS, osuProviders); if (messageIsNull(resultMsg)) return new HashMap<>(); Map result = (Map) resultMsg.obj; resultMsg.recycle(); return result; } /** * Returns the corresponding wifi configurations for given FQDN (Fully Qualified Domain Name) * list. * * An empty list will be returned when no match is found. * * @param fqdnList a list of FQDN * @param channel AsyncChannel to use for the response * @return List of {@link WifiConfiguration} converted from * {@link com.android.server.wifi.hotspot2.PasspointProvider} */ @NonNull public List syncGetWifiConfigsForPasspointProfiles(List fqdnList, AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously( CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES, fqdnList); if (messageIsNull(resultMsg)) return new ArrayList<>(); List result = (List) resultMsg.obj; resultMsg.recycle(); return result; } /** * Add or update a Passpoint configuration synchronously. * * @param channel Channel for communicating with the state machine * @param config The configuration to add or update * @param packageName Package name of the app adding/updating {@code config}. * @return true on success */ public boolean syncAddOrUpdatePasspointConfig(AsyncChannel channel, PasspointConfiguration config, int uid, String packageName) { Bundle bundle = new Bundle(); bundle.putInt(EXTRA_UID, uid); bundle.putString(EXTRA_PACKAGE_NAME, packageName); bundle.putParcelable(EXTRA_PASSPOINT_CONFIGURATION, config); Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG, bundle); if (messageIsNull(resultMsg)) return false; boolean result = (resultMsg.arg1 == SUCCESS); resultMsg.recycle(); return result; } /** * Remove a Passpoint configuration synchronously. * * @param channel Channel for communicating with the state machine * @param fqdn The FQDN of the Passpoint configuration to remove * @return true on success */ public boolean syncRemovePasspointConfig(AsyncChannel channel, String fqdn) { Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_PASSPOINT_CONFIG, fqdn); if (messageIsNull(resultMsg)) return false; boolean result = (resultMsg.arg1 == SUCCESS); resultMsg.recycle(); return result; } /** * Get the list of installed Passpoint configurations synchronously. * * @param channel Channel for communicating with the state machine * @return List of {@link PasspointConfiguration} */ public List syncGetPasspointConfigs(AsyncChannel channel) { Message resultMsg = channel.sendMessageSynchronously(CMD_GET_PASSPOINT_CONFIGS); if (messageIsNull(resultMsg)) return null; List result = (List) resultMsg.obj; resultMsg.recycle(); return result; } /** * 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) { supportedFeatureSet &= ~(WifiManager.WIFI_FEATURE_D2D_RTT | WifiManager.WIFI_FEATURE_D2AP_RTT); } 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; } /** * Delete a network * * @param networkId id of the network to be removed */ public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) { Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId); if (messageIsNull(resultMsg)) return false; boolean result = (resultMsg.arg1 != FAILURE); resultMsg.recycle(); return result; } /** * Enable a network * * @param netId network id of the network * @param disableOthers true, if all other networks have to be disabled * @return {@code true} if the operation succeeds, {@code false} otherwise */ public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) { Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId, disableOthers ? 1 : 0); if (messageIsNull(resultMsg)) return false; boolean result = (resultMsg.arg1 != FAILURE); resultMsg.recycle(); return result; } /** * Disable a network * * @param netId network id of the network * @return {@code true} if the operation succeeds, {@code false} otherwise */ public boolean syncDisableNetwork(AsyncChannel channel, int netId) { Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId); boolean result = (resultMsg.what != WifiManager.DISABLE_NETWORK_FAILED); if (messageIsNull(resultMsg)) return false; 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); } /** * Send a message indicating a package has been uninstalled. */ public void removeAppConfigs(String packageName, int uid) { // Build partial AppInfo manually - package may not exist in database any more ApplicationInfo ai = new ApplicationInfo(); ai.packageName = packageName; ai.uid = uid; sendMessage(CMD_REMOVE_APP_CONFIGURATIONS, ai); } /** * Send a message indicating a user has been removed. */ public void removeUserConfigs(int userId) { sendMessage(CMD_REMOVE_USER_CONFIGURATIONS, userId); } /** * Update the BatteryStats WorkSource. */ public void updateBatteryWorkSource(WorkSource newSource) { synchronized (mRunningWifiUids) { try { if (newSource != null) { mRunningWifiUids.set(newSource); } if (mIsRunning) { if (mReportedRunning) { // If the work source has changed since last time, need // to remove old work from battery stats. if (!mLastRunningWifiUids.equals(mRunningWifiUids)) { mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids, mRunningWifiUids); mLastRunningWifiUids.set(mRunningWifiUids); } } else { // Now being started, report it. mBatteryStats.noteWifiRunning(mRunningWifiUids); mLastRunningWifiUids.set(mRunningWifiUids); mReportedRunning = true; } } else { if (mReportedRunning) { // Last reported we were running, time to stop. mBatteryStats.noteWifiStopped(mLastRunningWifiUids); mLastRunningWifiUids.clear(); mReportedRunning = false; } } mWakeLock.setWorkSource(newSource); } catch (RemoteException ignore) { } } } /** * 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("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); } /** * Trigger message to handle user switch event. */ public void handleUserSwitch(int userId) { sendMessage(CMD_USER_SWITCH, userId); } /** * Trigger message to handle user unlock event. */ public void handleUserUnlock(int userId) { sendMessage(CMD_USER_UNLOCK, userId); } /** * Trigger message to handle user stop event. */ public void handleUserStop(int userId) { sendMessage(CMD_USER_STOP, userId); } /** * ****************************************************** * 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 WifiManager.SAVE_NETWORK: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); config = (WifiConfiguration) msg.obj; if (config != null) { sb.append(" ").append(config.configKey()); 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"); } if (config.selfAdded) { sb.append(" selfAdded"); } sb.append(" cuid=").append(config.creatorUid); sb.append(" suid=").append(config.lastUpdateUid); } break; case WifiManager.FORGET_NETWORK: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); config = (WifiConfiguration) msg.obj; if (config != null) { sb.append(" ").append(config.configKey()); sb.append(" nid=").append(config.networkId); if (config.hiddenSSID) { sb.append(" hidden"); } if (config.preSharedKey != null) { sb.append(" hasPSK"); } if (config.ephemeral) { sb.append(" ephemeral"); } if (config.selfAdded) { sb.append(" selfAdded"); } sb.append(" cuid=").append(config.creatorUid); sb.append(" suid=").append(config.lastUpdateUid); WifiConfiguration.NetworkSelectionStatus netWorkSelectionStatus = config.getNetworkSelectionStatus(); sb.append(" ajst=").append( netWorkSelectionStatus.getNetworkStatusString()); } 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.configKey()); } key = mWifiConfigManager.getLastSelectedNetworkConfigKey(); if (key != null) { sb.append(" last=").append(key); } break; case CMD_TARGET_BSSID: case CMD_ASSOCIATED_BSSID: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); if (msg.obj != null) { sb.append(" BSSID=").append((String) msg.obj); } if (mTargetRoamBSSID != null) { sb.append(" Target=").append(mTargetRoamBSSID); } sb.append(" roam=").append(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 WifiManager.RSSI_PKTCNT_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.score); sb.append(" link=").append(mWifiInfo.getLinkSpeed()); sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate)); sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate)); sb.append(String.format(" %.1f ", mWifiInfo.txBadRate)); sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate)); sb.append(String.format(" bcn=%d", mRunningBeaconCount)); report = reportOnTime(); if (report != null) { sb.append(" ").append(report); } sb.append(String.format(" score=%d", mWifiInfo.score)); break; case CMD_START_CONNECT: case WifiManager.CONNECT_NETWORK: 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.configKey()); } if (mTargetRoamBSSID != null) { sb.append(" ").append(mTargetRoamBSSID); } sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming)); config = getCurrentWifiConfiguration(); if (config != null) { sb.append(config.configKey()); } 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_ADD_OR_UPDATE_NETWORK: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); if (msg.obj != null) { config = (WifiConfiguration) msg.obj; sb.append(" ").append(config.configKey()); sb.append(" prio=").append(config.priority); sb.append(" status=").append(config.status); if (config.BSSID != null) { sb.append(" ").append(config.BSSID); } WifiConfiguration curConfig = getCurrentWifiConfiguration(); if (curConfig != null) { if (curConfig.configKey().equals(config.configKey())) { sb.append(" is current"); } else { sb.append(" current=").append(curConfig.configKey()); sb.append(" prio=").append(curConfig.priority); sb.append(" status=").append(curConfig.status); } } } break; case WifiManager.DISABLE_NETWORK: case CMD_ENABLE_NETWORK: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); key = mWifiConfigManager.getLastSelectedNetworkConfigKey(); if (key != null) { sb.append(" last=").append(key); } config = mWifiConfigManager.getConfiguredNetwork(msg.arg1); if (config != null && (key == null || !config.configKey().equals(key))) { sb.append(" target=").append(key); } break; case CMD_GET_CONFIGURED_NETWORKS: sb.append(" "); sb.append(Integer.toString(msg.arg1)); sb.append(" "); sb.append(Integer.toString(msg.arg2)); sb.append(" num=").append(mWifiConfigManager.getConfiguredNetworks().size()); 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_USER_SWITCH: sb.append(" userId="); sb.append(Integer.toString(msg.arg1)); 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 WifiManager.DISABLE_NETWORK: s = "DISABLE_NETWORK"; break; case WifiManager.CONNECT_NETWORK: s = "CONNECT_NETWORK"; break; case WifiManager.SAVE_NETWORK: s = "SAVE_NETWORK"; break; case WifiManager.FORGET_NETWORK: s = "FORGET_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 WifiManager.RSSI_PKTCNT_FETCH: s = "RSSI_PKTCNT_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() + " suppState:" + mSupplicantStateTracker.getSupplicantStateName()); } 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 = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS); 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.txBadRate = 0; mWifiInfo.txSuccessRate = 0; mWifiInfo.txRetriesRate = 0; mWifiInfo.rxSuccessRate = 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) { try { mBatteryStats.noteWifiRssiChanged(newRssi); } catch (RemoteException e) { // Won't happen. } StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS)); 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.LINK_CONFIGURATION_CHANGED_ACTION); 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); } else { // Reset parameters according to WifiInfo.reset() mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); mWifiInfo.setBSSID(null); mWifiInfo.setSSID(null); } 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.setNetworkSuggestionOrSpecifierPackageName(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); } } } } mSupplicantStateTracker.sendMessage(Message.obtain(message)); mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo); return state; } /** * Tells IpClient what L2Key and GroupHint to use for IpMemoryStore. */ private void updateL2KeyAndGroupHint() { if (mIpClient != null) { Pair p = mWifiScoreCard.getL2KeyAndGroupHint(mWifiInfo); if (!p.equals(mLastL2KeyAndGroupHint)) { if (mIpClient.setL2KeyAndGroupHint(p.first, p.second)) { mLastL2KeyAndGroupHint = p; } else { mLastL2KeyAndGroupHint = null; } } } } private @Nullable Pair 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; 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. *

* 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. *

* 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); } // 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 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 = NetworkUtils.inetAddressToInt(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); } // 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; } MacAddress currentMac = MacAddress.fromString(mWifiNative.getMacAddress(mInterfaceName)); MacAddress newMac = config.getOrCreateRandomizedMacAddress(); mWifiConfigManager.setNetworkRandomizedMacAddress(config.networkId, newMac); if (!WifiConfiguration.isValidMacAddressForRandomization(newMac)) { Log.wtf(TAG, "Config generated an invalid MAC address"); } else if (currentMac.equals(newMac)) { 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 " + currentMac.toString() + " = " + 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 mConnectedMacRandomzationSupported; } /** * 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; 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); break; case CMD_ENABLE_NETWORK: boolean disableOthers = message.arg2 == 1; int netId = message.arg1; boolean ok = mWifiConfigManager.enableNetwork( netId, disableOthers, message.sendingUid); if (!ok) { mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; } replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); break; case CMD_ADD_OR_UPDATE_NETWORK: WifiConfiguration config = (WifiConfiguration) message.obj; NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); if (!result.isSuccess()) { mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; } replyToMessage(message, message.what, result.getNetworkId()); break; case CMD_REMOVE_NETWORK: deleteNetworkConfigAndSendReply(message, false); break; case CMD_GET_CONFIGURED_NETWORKS: replyToMessage(message, message.what, mWifiConfigManager.getSavedNetworks(message.arg2)); break; case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS: replyToMessage(message, message.what, mWifiConfigManager.getConfiguredNetworksWithPasswords()); 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(); mPasspointManager.initializeProvisioner( mWifiInjector.getWifiServiceHandlerThread().getLooper()); replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); break; case CMD_BOOT_COMPLETED: // get other services that we need to manage getAdditionalWifiServiceInterfaces(); new MemoryStoreImpl(mContext, mWifiInjector, mWifiScoreCard).start(); if (!mWifiConfigManager.loadFromStore()) { Log.e(TAG, "Failed to load from config store"); } registerNetworkFactory(); 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 CMD_TARGET_BSSID: case CMD_START_CONNECT: case CMD_START_ROAM: case CMD_ASSOCIATED_BSSID: case CMD_UNWANTED_NETWORK: case CMD_DISCONNECTING_WATCHDOG_TIMER: case CMD_ROAM_WATCHDOG_TIMER: case CMD_DISABLE_EPHEMERAL_NETWORK: mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; break; 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 WifiManager.CONNECT_NETWORK: replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.BUSY); break; case WifiManager.FORGET_NETWORK: deleteNetworkConfigAndSendReply(message, true); break; case WifiManager.SAVE_NETWORK: saveNetworkConfigAndSendReply(message); break; case WifiManager.DISABLE_NETWORK: replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, WifiManager.BUSY); break; case WifiManager.RSSI_PKTCNT_FETCH: replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED, 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_GET_MATCHING_OSU_PROVIDERS: replyToMessage(message, message.what, new HashMap<>()); break; case CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS: replyToMessage(message, message.what, new HashMap()); break; case CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES: replyToMessage(message, message.what, new ArrayList<>()); 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_REMOVE_APP_CONFIGURATIONS: deferMessage(message); break; case CMD_REMOVE_USER_CONFIGURATIONS: deferMessage(message); 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_USER_SWITCH: Set removedNetworkIds = mWifiConfigManager.handleUserSwitch(message.arg1); if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network sendMessage(CMD_DISCONNECT); } break; case CMD_USER_UNLOCK: mWifiConfigManager.handleUserUnlock(message.arg1); break; case CMD_USER_STOP: mWifiConfigManager.handleUserStop(message.arg1); break; case CMD_QUERY_OSU_ICON: case CMD_MATCH_PROVIDER_NETWORK: /* 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_ADD_OR_UPDATE_PASSPOINT_CONFIG: Bundle bundle = (Bundle) message.obj; int addResult = mPasspointManager.addOrUpdateProvider(bundle.getParcelable( EXTRA_PASSPOINT_CONFIGURATION), bundle.getInt(EXTRA_UID), bundle.getString(EXTRA_PACKAGE_NAME)) ? SUCCESS : FAILURE; replyToMessage(message, message.what, addResult); break; case CMD_REMOVE_PASSPOINT_CONFIG: int removeResult = mPasspointManager.removeProvider( (String) message.obj) ? SUCCESS : FAILURE; replyToMessage(message, message.what, removeResult); break; case CMD_GET_PASSPOINT_CONFIGS: replyToMessage(message, message.what, mPasspointManager.getProviderConfigs()); 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 CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS: replyToMessage(message, message.what, new HashMap<>()); 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; 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.startLogging(mVerboseLoggingEnabled); mIsRunning = true; updateBatteryWorkSource(null); /** * Enable bluetooth coexistence scan mode when bluetooth connection is active. * When this mode is on, some of the low-level scan parameters used by the * driver are changed to reduce interference with bluetooth */ 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(); mIsRunning = false; updateBatteryWorkSource(null); 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); // Inform sar manager that wifi is Enabled mSarManager.setClientWifiState(WifiManager.WIFI_STATE_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); // Inform sar manager that wifi is being disabled mSarManager.setClientWifiState(WifiManager.WIFI_STATE_DISABLED); 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 removedNetworkIds; int reasonCode; boolean timedOut; boolean handleStatus = HANDLED; 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; } if (bssid != null) { // If we have a BSSID, tell configStore to black list it mDidBlackListBSSID = mWifiConnectivityManager.trackBssid(bssid, false, reasonCode); } mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId, WifiConfiguration.NetworkSelectionStatus .DISABLED_ASSOCIATION_REJECTION); mWifiConfigManager.setRecentFailureAssociationStatus(mTargetNetworkId, reasonCode); mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT); // 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, WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); mWifiInjector.getWifiLastResortWatchdog() .noteConnectionFailureAndTriggerIfNeeded( getTargetSsid(), bssid, WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); break; case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: mWifiDiagnostics.captureBugReportData( WifiDiagnostics.REPORT_REASON_AUTH_FAILURE); mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT); 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. int level2FailureReason; 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(), mTargetRoamBSSID, 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 CMD_REMOVE_NETWORK: if (!deleteNetworkConfigAndSendReply(message, false)) { // failed to remove the config and caller was notified mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; break; } // we successfully deleted the network config netId = message.arg1; if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network sendMessage(CMD_DISCONNECT); } break; case CMD_ENABLE_NETWORK: boolean disableOthers = message.arg2 == 1; netId = message.arg1; if (disableOthers) { // If the app has all the necessary permissions, this will trigger a connect // attempt. ok = connectToUserSelectNetwork(netId, message.sendingUid, false); } else { ok = mWifiConfigManager.enableNetwork(netId, false, message.sendingUid); } if (!ok) { mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; } replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); break; case WifiManager.DISABLE_NETWORK: netId = message.arg1; if (mWifiConfigManager.disableNetwork(netId, message.sendingUid)) { replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED); if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network sendMessage(CMD_DISCONNECT); } } else { loge("Failed to disable network"); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, WifiManager.ERROR); } break; case CMD_DISABLE_EPHEMERAL_NETWORK: config = mWifiConfigManager.disableEphemeralNetwork((String) message.obj); if (config != null) { if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network sendMessage(CMD_DISCONNECT); } } 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 && TelephonyUtil.isSimConfig(mTargetWifiConfiguration)) { // Pair Pair identityPair = TelephonyUtil.getSimIdentity(getTelephonyManager(), new TelephonyUtil(), mTargetWifiConfiguration, mWifiInjector.getCarrierNetworkConfig()); Log.i(TAG, "SUP_REQUEST_IDENTITY: identityPair=" + identityPair); if (identityPair != null && identityPair.first != null) { mWifiNative.simIdentityResponse(mInterfaceName, netId, 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_GET_MATCHING_OSU_PROVIDERS: replyToMessage(message, message.what, mPasspointManager.getMatchingOsuProviders( (List) message.obj)); break; case CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS: replyToMessage(message, message.what, mPasspointManager.getMatchingPasspointConfigsForOsuProviders( (List) message.obj)); break; case CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES: replyToMessage(message, message.what, mPasspointManager.getWifiConfigsForPasspointProfiles( (List) message.obj)); 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 sup state " + mSupplicantStateTracker.getSupplicantStateName() + " 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); reportConnectionAttemptStart(config, mTargetRoamBSSID, WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED); if (config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT && mConnectedMacRandomzationSupported) { 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 && TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod()) && mWifiInjector.getCarrierNetworkConfig() .isCarrierEncryptionInfoAvailable() && TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) { String anonAtRealm = TelephonyUtil.getAnonymousIdentityWith3GppRealm( getTelephonyManager()); // Use anonymous@ 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); replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); break; } break; case CMD_REMOVE_APP_CONFIGURATIONS: removedNetworkIds = mWifiConfigManager.removeNetworksForApp((ApplicationInfo) message.obj); if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network. sendMessage(CMD_DISCONNECT); } break; case CMD_REMOVE_USER_CONFIGURATIONS: removedNetworkIds = mWifiConfigManager.removeNetworksForUser((Integer) message.arg1); if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network. sendMessage(CMD_DISCONNECT); } break; case WifiManager.CONNECT_NETWORK: /** * 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 */ netId = message.arg1; config = (WifiConfiguration) message.obj; boolean hasCredentialChanged = false; // New network addition. if (config != null) { result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); if (!result.isSuccess()) { loge("CONNECT_NETWORK adding/updating config=" + config + " failed"); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); break; } netId = result.getNetworkId(); hasCredentialChanged = result.hasCredentialChanged(); } if (!connectToUserSelectNetwork( netId, message.sendingUid, hasCredentialChanged)) { mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.NOT_AUTHORIZED); break; } mWifiMetrics.logStaEvent(StaEvent.TYPE_CONNECT_NETWORK, config); broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); break; case WifiManager.SAVE_NETWORK: result = saveNetworkConfigAndSendReply(message); netId = result.getNetworkId(); if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) { if (result.hasCredentialChanged()) { config = (WifiConfiguration) message.obj; // The network credentials changed and we're connected to this network, // start a new connection with the updated credentials. logi("SAVE_NETWORK credential changed for config=" + config.configKey() + ", Reconnecting."); startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY); } else { if (result.hasProxyChanged()) { if (mIpClient != null) { log("Reconfiguring proxy on connection"); mIpClient.setHttpProxy( getCurrentWifiConfiguration().getHttpProxy()); } } 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"); // TODO(b/36576642): clear addresses and disable IPv6 // to simplify obtainingIpState. transitionTo(mObtainingIpState); } } } break; case WifiManager.FORGET_NETWORK: if (!deleteNetworkConfigAndSendReply(message, true)) { // Caller was notified of failure, nothing else to do break; } // the network was deleted netId = message.arg1; if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network sendMessage(CMD_DISCONNECT); } break; case CMD_ASSOCIATED_BSSID: // 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)); } } 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); } } mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode); // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA' if (config.enterpriseConfig != null && TelephonyUtil.isSimEapMethod( config.enterpriseConfig.getEapMethod())) { String anonymousIdentity = mWifiNative.getEapAnonymousIdentity(mInterfaceName); if (mVerboseLoggingEnabled) { log("EAP Pseudonym: " + anonymousIdentity); } if (!TelephonyUtil.isAnonymousAtRealmIdentity(anonymousIdentity)) { // Save the pseudonym only if it is a real one config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity); 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 CMD_MATCH_PROVIDER_NETWORK: // TODO(b/31065385): Passpoint config management. replyToMessage(message, message.what, 0); break; case CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG: Bundle bundle = (Bundle) message.obj; PasspointConfiguration passpointConfig = bundle.getParcelable( EXTRA_PASSPOINT_CONFIGURATION); if (mPasspointManager.addOrUpdateProvider(passpointConfig, bundle.getInt(EXTRA_UID), bundle.getString(EXTRA_PACKAGE_NAME))) { String fqdn = passpointConfig.getHomeSp().getFqdn(); if (isProviderOwnedNetwork(mTargetNetworkId, fqdn) || isProviderOwnedNetwork(mLastNetworkId, fqdn)) { logd("Disconnect from current network since its provider is updated"); sendMessage(CMD_DISCONNECT); } replyToMessage(message, message.what, SUCCESS); } else { replyToMessage(message, message.what, FAILURE); } break; case CMD_REMOVE_PASSPOINT_CONFIG: String fqdn = (String) message.obj; if (mPasspointManager.removeProvider(fqdn)) { if (isProviderOwnedNetwork(mTargetNetworkId, fqdn) || isProviderOwnedNetwork(mLastNetworkId, fqdn)) { logd("Disconnect from current network since its provider is removed"); sendMessage(CMD_DISCONNECT); } mWifiConfigManager.removePasspointConfiguredNetwork(fqdn); replyToMessage(message, message.what, SUCCESS); } else { replyToMessage(message, message.what, FAILURE); } break; case CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS: replyToMessage(message, message.what, mPasspointManager.getAllMatchingFqdnsForScanResults( (List) message.obj)); break; case CMD_TARGET_BSSID: // 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) { mPasspointManager.removeEphemeralProviders(); mWifiConfigManager.resetSimNetworks(); } break; case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: mBluetoothConnectionActive = (message.arg1 != BluetoothAdapter.STATE_DISCONNECTED); mWifiNative.setBluetoothCoexistenceScanMode( mInterfaceName, 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(WifiSsid.NONE)) { result.setSSID(mWifiInfo.getSSID()); } else { result.setSSID(null); } Pair 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)); } /** * Checks if the given network |networkdId| is provided by the given Passpoint provider with * |providerFqdn|. * * @param networkId The ID of the network to check * @param providerFqdn The FQDN of the Passpoint provider * @return true if the given network is provided by the given Passpoint provider */ private boolean isProviderOwnedNetwork(int networkId, String providerFqdn) { if (networkId == WifiConfiguration.INVALID_NETWORK_ID) { return false; } WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId); if (config == null) { return false; } return TextUtils.equals(config.FQDN, providerFqdn); } 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) { getTelephonyManager() .createForSubscriptionId( SubscriptionManager.getDefaultDataSubscriptionId()) .resetCarrierKeysForImsiEncryption(); } 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.score)); } 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.score)); } unwantedNetwork(NETWORK_STATUS_UNWANTED_VALIDATION_FAILED); } else if (status == NetworkAgent.VALID_NETWORK) { if (mVerboseLoggingEnabled) { log("WifiNetworkAgent -> Wifi networkStatus valid, score= " + Integer.toString(mWifiInfo.score)); } 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); } // rfc4186 & rfc4187: // create Permanent Identity base on IMSI, // identity = usernam@realm // with username = prefix | IMSI // and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003) private String buildIdentity(int eapMethod, String imsi, String mccMnc) { String mcc; String mnc; String prefix; if (imsi == null || imsi.isEmpty()) { return ""; } if (eapMethod == WifiEnterpriseConfig.Eap.SIM) { prefix = "1"; } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA) { prefix = "0"; } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME) { prefix = "6"; } else { // not a valid EapMethod return ""; } /* extract mcc & mnc from mccMnc */ if (mccMnc != null && !mccMnc.isEmpty()) { mcc = mccMnc.substring(0, 3); mnc = mccMnc.substring(3); if (mnc.length() == 2) { mnc = "0" + mnc; } } else { // extract mcc & mnc from IMSI, assume mnc size is 3 mcc = imsi.substring(0, 3); mnc = imsi.substring(3, 6); } return prefix + imsi + "@wlan.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org"; } 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); } @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; 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(), mTargetRoamBSSID, 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(), mTargetRoamBSSID, 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; /* Ignore connection to same network */ case WifiManager.CONNECT_NETWORK: int netId = message.arg1; if (mWifiInfo.getNetworkId() == netId) { replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); break; } handleStatus = NOT_HANDLED; 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); if (statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) { mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD, convertToUsabilityStatsTriggerType(statusDataStall), -1); } 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 WifiManager.RSSI_PKTCNT_FETCH: RssiPacketCountInfo info = new RssiPacketCountInfo(); fetchRssiLinkSpeedAndFrequencyNative(); info.rssi = mWifiInfo.getRssi(); WifiNative.TxPacketCounters counters = mWifiNative.getTxPacketCounters(mInterfaceName); if (counters != null) { info.txgood = counters.txSucceeded; info.txbad = counters.txFailed; replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info); } else { replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED, WifiManager.ERROR); } break; case CMD_ASSOCIATED_BSSID: 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 (TelephonyUtil.isSimConfig(config)) { mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, StaEvent.DISCONNECT_RESET_SIM_NETWORKS); // TODO(b/132385576): STA may immediately connect back to the network // that we just disconnected from mWifiNative.disconnect(mInterfaceName); 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.configKey(); 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. // // TODO: Only ever enter this state the first time we connect to a // network, never on switching between static configuration and // DHCP. 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(mTcpBufferSizes)) { mIpClient.setTcpBufferSizes(mTcpBufferSizes); } } final ProvisioningConfiguration prov; if (!isUsingStaticIp) { prov = new ProvisioningConfiguration.Builder() .withPreDhcpAction() .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName)) .withNetwork(getCurrentNetwork()) .withDisplayName(currentConfig.SSID) .withRandomMacAddress() .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 CMD_START_CONNECT: case CMD_START_ROAM: mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; break; case WifiManager.SAVE_NETWORK: mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED; deferMessage(message); break; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE, WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); 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; mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode); sendNetworkStateChangeBroadcast(mLastBssid); // Successful framework roam! (probably) 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, WifiConfiguration.NetworkSelectionStatus .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, WifiConfiguration.NetworkSelectionStatus .DISABLED_NO_INTERNET_TEMPORARY); } } } } 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); 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 CMD_ASSOCIATED_BSSID: // 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); } 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 " + mSupplicantStateTracker.getSupplicantStateName() + " my state " + getCurrentState().getName() + " nid=" + Integer.toString(netId) + " config " + config.configKey() + " 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); replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); 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_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 */ 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 *

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 = TelephonyUtil.getGsmSimAuthResponse(requestData.data, getTelephonyManager()); if (response == null) { // In case of failure, issue may be due to sim type, retry as No.2 case response = TelephonyUtil.getGsmSimpleSimAuthResponse(requestData.data, getTelephonyManager()); if (response == null) { // In case of failure, issue may be due to sim type, retry as No.3 case response = TelephonyUtil.getGsmSimpleSimNoLengthAuthResponse(requestData.data, getTelephonyManager()); } } if (response == null || response.length() == 0) { mWifiNative.simAuthFailedResponse(mInterfaceName, requestData.networkId); } else { logv("Supplicant Response -" + response); mWifiNative.simAuthResponse( mInterfaceName, requestData.networkId, 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 = TelephonyUtil.get3GAuthResponse(requestData, getTelephonyManager()); if (response != null) { mWifiNative.simAuthResponse( mInterfaceName, requestData.networkId, response.type, response.response); } else { mWifiNative.umtsAuthFailedResponse(mInterfaceName, requestData.networkId); } } /** * 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 } /** * Update WifiMetrics before dumping */ public void updateWifiMetrics() { mWifiMetrics.updateSavedNetworks(mWifiConfigManager.getSavedNetworks(Process.WIFI_UID)); mPasspointManager.updateMetrics(); } /** * Private method to handle calling WifiConfigManager to forget/remove network configs and reply * to the message from the sender of the outcome. * * The current implementation requires that forget and remove be handled in different ways * (responses are handled differently). In the interests of organization, the handling is all * now in this helper method. TODO: b/35257965 is filed to track the possibility of merging * the two call paths. */ private boolean deleteNetworkConfigAndSendReply(Message message, boolean calledFromForget) { boolean success = mWifiConfigManager.removeNetwork(message.arg1, message.sendingUid); if (!success) { loge("Failed to remove network"); } if (calledFromForget) { if (success) { replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED); broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT, (WifiConfiguration) message.obj); return true; } replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED, WifiManager.ERROR); return false; } else { // Remaining calls are from the removeNetwork path if (success) { replyToMessage(message, message.what, SUCCESS); return true; } mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, message.what, FAILURE); return false; } } /** * Private method to handle calling WifiConfigManager to add & enable network configs and reply * to the message from the sender of the outcome. * * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error. */ private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) { WifiConfiguration config = (WifiConfiguration) message.obj; if (config == null) { loge("SAVE_NETWORK with null configuration " + mSupplicantStateTracker.getSupplicantStateName() + " my state " + getCurrentState().getName()); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); } NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); if (!result.isSuccess()) { loge("SAVE_NETWORK adding/updating config=" + config + " failed"); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); return result; } if (!mWifiConfigManager.enableNetwork( result.getNetworkId(), false, message.sendingUid)) { loge("SAVE_NETWORK enabling config=" + config + " failed"); mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); } broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); return result; } private static String getLinkPropertiesSummary(LinkProperties lp) { List 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 (!mConnectedMacRandomzationSupported) { 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); } }