diff options
author | AnjaneeDevi Kapparapu <c_akappa@qti.qualcomm.com> | 2016-12-09 14:23:11 +0530 |
---|---|---|
committer | Anjaneedevi Kapparapu <akappa@codeaurora.org> | 2016-12-09 14:32:38 +0530 |
commit | 3ce645f274856faa76d263fbe6ddd32bda36f54a (patch) | |
tree | 2211f3742f423259c27a3d85c84896404051cc85 | |
parent | 7370404484797419d889ef09bd5a7b7a0db1d1f0 (diff) | |
parent | 173bf91cfe966d84dc42e3444140c692975278c6 (diff) | |
download | android_frameworks_opt_net_wifi-3ce645f274856faa76d263fbe6ddd32bda36f54a.tar.gz android_frameworks_opt_net_wifi-3ce645f274856faa76d263fbe6ddd32bda36f54a.tar.bz2 android_frameworks_opt_net_wifi-3ce645f274856faa76d263fbe6ddd32bda36f54a.zip |
To Backport the N-Mr1 Changes
To Backport the N-Mr1 Changes
Merge remote-tracking branch 'origin/wlan-aosp.lnx.2.0.c1-rel' into wlan-aosp.lnx.2.0.c1-dev
Change-Id: I7ff8442f627deb70400ad84a8712c27dffdbcda1
CRs-Fixed: 1099457
52 files changed, 3724 insertions, 1170 deletions
diff --git a/service/Android.mk b/service/Android.mk index c1ba4a886..cdee104a4 100644 --- a/service/Android.mk +++ b/service/Android.mk @@ -145,6 +145,12 @@ LOCAL_MODULE_TAGS := LOCAL_MODULE := wifi-service LOCAL_PROTOC_OPTIMIZE_TYPE := nano +ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true) +LOCAL_EMMA_INSTRUMENT := true +endif + +LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.server.wifi.* + include $(BUILD_JAVA_LIBRARY) endif diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java index c2579e089..cb03f7a2b 100644 --- a/service/java/com/android/server/wifi/FrameworkFacade.java +++ b/service/java/com/android/server/wifi/FrameworkFacade.java @@ -49,8 +49,9 @@ public class FrameworkFacade { } public BaseWifiLogger makeRealLogger( - WifiStateMachine stateMachine, WifiNative wifiNative, BuildProperties buildProperties) { - return new WifiLogger(stateMachine, wifiNative, buildProperties); + Context context, WifiStateMachine stateMachine, WifiNative wifiNative, + BuildProperties buildProperties) { + return new WifiLogger(context, stateMachine, wifiNative, buildProperties); } public boolean setIntegerSetting(Context context, String name, int def) { @@ -144,7 +145,7 @@ public class FrameworkFacade { String countryCode, ArrayList<Integer> allowed2GChannels, SoftApManager.Listener listener) { return new SoftApManager( - context, looper, wifiNative, nmService, cm, countryCode, + looper, wifiNative, nmService, countryCode, allowed2GChannels, listener); } diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java index 1a5a923c0..dc87a5bd1 100644 --- a/service/java/com/android/server/wifi/ScanDetail.java +++ b/service/java/com/android/server/wifi/ScanDetail.java @@ -59,6 +59,9 @@ public class ScanDetail { if (networkDetail.is80211McResponderSupport()) { mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER); } + if (networkDetail.isInterworking()) { + mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); + } mMatches = null; } diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java index e3f31920e..cb44e2af9 100644 --- a/service/java/com/android/server/wifi/ScanDetailCache.java +++ b/service/java/com/android/server/wifi/ScanDetailCache.java @@ -85,11 +85,6 @@ public class ScanDetailCache { return size() == 0; } - ScanDetail getFirst() { - Iterator<ScanDetail> it = mMap.values().iterator(); - return it.hasNext() ? it.next() : null; - } - Collection<String> keySet() { return mMap.keySet(); } diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java index 4fdc6bfb0..98dad2bec 100644 --- a/service/java/com/android/server/wifi/SoftApManager.java +++ b/service/java/com/android/server/wifi/SoftApManager.java @@ -20,14 +20,8 @@ import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC; import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL; import static com.android.server.wifi.util.ApConfigUtil.SUCCESS; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.net.ConnectivityManager; -import android.net.InterfaceConfiguration; -import android.net.LinkAddress; -import android.net.NetworkUtils; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.INetworkManagementService; @@ -41,6 +35,7 @@ import com.android.server.wifi.util.ApConfigUtil; import java.util.ArrayList; import java.util.Locale; +import android.os.UserHandle; /** * Manage WiFi in AP mode. @@ -48,12 +43,9 @@ import java.util.Locale; */ public class SoftApManager { private static final String TAG = "SoftApManager"; - private boolean restartSap = false; - private final Context mContext; private final INetworkManagementService mNmService; private final WifiNative mWifiNative; - private final ConnectivityManager mConnectivityManager; private final ArrayList<Integer> mAllowed2GChannels; private final String mCountryCode; @@ -67,16 +59,6 @@ public class SoftApManager { private final Listener mListener; - private static class TetherStateChange { - public ArrayList<String> available; - public ArrayList<String> active; - - TetherStateChange(ArrayList<String> av, ArrayList<String> ac) { - available = av; - active = ac; - } - } - /** * Listener for soft AP state changes. */ @@ -89,40 +71,21 @@ public class SoftApManager { void onStateChanged(int state, int failureReason); } - public SoftApManager(Context context, - Looper looper, + public SoftApManager(Looper looper, WifiNative wifiNative, INetworkManagementService nmService, - ConnectivityManager connectivityManager, String countryCode, ArrayList<Integer> allowed2GChannels, Listener listener) { mStateMachine = new SoftApStateMachine(looper); - mContext = context; mNmService = nmService; mWifiNative = wifiNative; - mConnectivityManager = connectivityManager; mCountryCode = countryCode; mAllowed2GChannels = allowed2GChannels; mListener = listener; mInterfaceName = mWifiNative.getInterfaceName(); - - /* Register receiver for tether state changes. */ - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - ArrayList<String> available = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_AVAILABLE_TETHER); - ArrayList<String> active = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_ACTIVE_TETHER); - mStateMachine.sendMessage( - SoftApStateMachine.CMD_TETHER_STATE_CHANGE, - new TetherStateChange(available, active)); - } - }, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); } /** @@ -239,116 +202,19 @@ public class SoftApManager { Log.d(TAG, "Soft AP is stopped"); } - private boolean startTethering(ArrayList<String> available) { - String[] wifiRegexs = mConnectivityManager.getTetherableWifiRegexs(); - - for (String intf : available) { - for (String regex : wifiRegexs) { - if (intf.matches(regex)) { - if (mCreateNewInterface) { - /** - * If we turn on SoftAp follwed by Wifi, sometimes TetherState - * Machine still advertise the list of interface as Up. But in - * concurrency case our interfcae for softap is softap0, - * hence start tethering only on softap0. - */ - if (!intf.matches(mInterfaceName)) { - Log.e(TAG,"For STA + SoftAp concurrency skip tethering on " + intf); - continue; - } - } - try { - InterfaceConfiguration ifcg = - mNmService.getInterfaceConfig(intf); - if (ifcg != null) { - /* IP/netmask: 192.168.43.1/255.255.255.0 */ - ifcg.setLinkAddress(new LinkAddress( - NetworkUtils.numericToInetAddress("192.168.43.1"), 24)); - ifcg.setInterfaceUp(); - - mNmService.setInterfaceConfig(intf, ifcg); - } - } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + intf + ", :" + e); - return false; - } - - if (mConnectivityManager.tether(intf) - != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "Error tethering on " + intf); - return false; - } - mTetherInterfaceName = intf; - return true; - } - } - } - /* We found no interfaces to tether. */ - return false; - } - - private void stopTethering() { - try { - /* Clear the interface address. */ - InterfaceConfiguration ifcg = - mNmService.getInterfaceConfig(mTetherInterfaceName); - if (ifcg != null) { - ifcg.setLinkAddress( - new LinkAddress( - NetworkUtils.numericToInetAddress("0.0.0.0"), 0)); - mNmService.setInterfaceConfig(mTetherInterfaceName, ifcg); - } - } catch (Exception e) { - Log.e(TAG, "Error resetting interface " + mTetherInterfaceName + ", :" + e); - } - - if (mConnectivityManager.untether(mTetherInterfaceName) - != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "Untether initiate failed!"); - } - } - - private boolean isWifiTethered(ArrayList<String> active) { - String[] wifiRegexs = mConnectivityManager.getTetherableWifiRegexs(); - for (String intf : active) { - for (String regex : wifiRegexs) { - if (intf.matches(regex)) { - return true; - } - } - } - /* No tethered interface. */ - return false; - } - private class SoftApStateMachine extends StateMachine { /* Commands for the state machine. */ public static final int CMD_START = 0; public static final int CMD_STOP = 1; - public static final int CMD_TETHER_STATE_CHANGE = 2; - public static final int CMD_TETHER_NOTIFICATION_TIMEOUT = 3; - - private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000; - - /* Sequence number used to track tether notification timeout. */ - private int mTetherToken = 0; private final State mIdleState = new IdleState(); private final State mStartedState = new StartedState(); - private final State mTetheringState = new TetheringState(); - private final State mTetheredState = new TetheredState(); - private final State mUntetheringState = new UntetheringState(); SoftApStateMachine(Looper looper) { super(TAG, looper); - // CHECKSTYLE:OFF IndentationCheck addState(mIdleState); - addState(mStartedState, mIdleState); - addState(mTetheringState, mStartedState); - addState(mTetheredState, mStartedState); - addState(mUntetheringState, mStartedState); - // CHECKSTYLE:ON IndentationCheck + addState(mStartedState, mIdleState); setInitialState(mIdleState); start(); @@ -388,62 +254,11 @@ public class SoftApManager { /* Already started, ignore this command. */ break; case CMD_STOP: - if (restartSap) { - stopSoftAp(); - updateApState(WifiManager.WIFI_AP_STATE_RESTART, 0); - restartSap = false; - } else { - updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); - stopSoftAp(); - updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); - } + updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); + stopSoftAp(); + updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); transitionTo(mIdleState); break; - case CMD_TETHER_STATE_CHANGE: - TetherStateChange stateChange = (TetherStateChange) message.obj; - if (startTethering(stateChange.available)) { - transitionTo(mTetheringState); - } - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - /** - * This is a transient state. We will transition out of this state when - * we receive a notification that WiFi is tethered (TetheredState) or - * we timed out waiting for that notification (StartedState). - */ - private class TetheringState extends State { - @Override - public void enter() { - /* Send a delayed message to terminate if tethering fails to notify. */ - sendMessageDelayed( - obtainMessage(CMD_TETHER_NOTIFICATION_TIMEOUT, ++mTetherToken), - TETHER_NOTIFICATION_TIME_OUT_MSECS); - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_TETHER_STATE_CHANGE: - TetherStateChange stateChange = (TetherStateChange) message.obj; - if (isWifiTethered(stateChange.active)) { - transitionTo(mTetheredState); - } - break; - case CMD_TETHER_NOTIFICATION_TIMEOUT: - if (message.arg1 == mTetherToken) { - Log.e(TAG, "Failed to get tether update, " - + "shutdown soft access point"); - transitionTo(mStartedState); - /* Needs to be first thing handled. */ - sendMessageAtFrontOfQueue(CMD_STOP); - } - break; default: return NOT_HANDLED; } @@ -451,73 +266,5 @@ public class SoftApManager { } } - private class TetheredState extends State { - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_TETHER_STATE_CHANGE: - TetherStateChange stateChange = (TetherStateChange) message.obj; - if (!isWifiTethered(stateChange.active)) { - Log.e(TAG, "Tether State Change : Restart (Stop and Start) Soft AP"); - restartSap = true; - sendMessage(CMD_STOP); - } - break; - case CMD_STOP: - Log.d(TAG, "Untethering before stopping AP"); - stopTethering(); - transitionTo(mUntetheringState); - break; - - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - /** - * This is a transient state, will transition out of this state to StartedState - * when we receive a notification that WiFi is untethered or we timed out waiting - * for that notification. - */ - private class UntetheringState extends State { - @Override - public void enter() { - /* Send a delayed message to terminate if tethering fails to notify. */ - sendMessageDelayed( - obtainMessage(CMD_TETHER_NOTIFICATION_TIMEOUT, ++mTetherToken), - TETHER_NOTIFICATION_TIME_OUT_MSECS); - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_TETHER_STATE_CHANGE: - TetherStateChange stateChange = (TetherStateChange) message.obj; - /* Transition back to StartedState when WiFi is untethered. */ - if (!isWifiTethered(stateChange.active)) { - transitionTo(mStartedState); - /* Needs to be first thing handled */ - sendMessageAtFrontOfQueue(CMD_STOP); - } - break; - case CMD_TETHER_NOTIFICATION_TIMEOUT: - if (message.arg1 == mTetherToken) { - Log.e(TAG, "Failed to get tether update, " - + "force stop access point"); - transitionTo(mStartedState); - /* Needs to be first thing handled. */ - sendMessageAtFrontOfQueue(CMD_STOP); - } - break; - default: - /* Defer handling of this message until untethering is completed. */ - deferMessage(message); - break; - } - return HANDLED; - } - } } } diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index cdcf583b4..5a248bee9 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -190,7 +190,7 @@ public class WifiConfigManager { */ private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = { -1, // threshold for NETWORK_SELECTION_ENABLE - 1, // threshold for DISABLED_BAD_LINK + 1, // threshold for DISABLED_BAD_LINK (deprecated) 5, // threshold for DISABLED_ASSOCIATION_REJECTION 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 5, // threshold for DISABLED_DHCP_FAILURE @@ -206,7 +206,7 @@ public class WifiConfigManager { */ private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = { Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE - 15, // threshold for DISABLED_BAD_LINK + 15, // threshold for DISABLED_BAD_LINK (deprecated) 5, // threshold for DISABLED_ASSOCIATION_REJECTION 5, // threshold for DISABLED_AUTHENTICATION_FAILURE 5, // threshold for DISABLED_DHCP_FAILURE @@ -243,11 +243,6 @@ public class WifiConfigManager { public AtomicInteger mBandAward5Ghz = new AtomicInteger(); /** - * If Connectivity Service has triggered an unwanted network disconnect - */ - public long mLastUnwantedNetworkDisconnectTimestamp = 0; - - /** * Framework keeps a list of ephemeral SSIDs that where deleted by user, * so as, framework knows not to autojoin again those SSIDs based on scorer input. * The list is never cleared up. @@ -408,7 +403,7 @@ public class WifiConfigManager { mIpconfigStore = new IpConfigStore(mWriter); mWifiNetworkHistory = new WifiNetworkHistory(context, mLocalLog, mWriter); mWifiConfigStore = - new WifiConfigStore(wifiNative, mKeyStore, mLocalLog, mShowNetworks, true); + new WifiConfigStore(context, wifiNative, mKeyStore, mLocalLog, mShowNetworks, true); } public void trimANQPCache(boolean all) { @@ -740,19 +735,13 @@ public class WifiConfigManager { } if (config.isPasspoint()) { - /* need to slap on the SSID of selected bssid to work */ - if (getScanDetailCache(config).size() != 0) { - ScanDetail result = getScanDetailCache(config).getFirst(); - if (result == null) { - loge("Could not find scan result for " + config.BSSID); - } else { - logd("Setting SSID for " + config.networkId + " to" + result.getSSID()); - setSSIDNative(config, result.getSSID()); - } - - } else { - loge("Could not find bssid for " + config); - } + // Set the SSID for the underlying WPA supplicant network entry corresponding to this + // Passpoint profile to the SSID of the BSS selected by QNS. |config.SSID| is set by + // selectQualifiedNetwork.selectQualifiedNetwork(), when the qualified network selected + // is a Passpoint network. + logd("Setting SSID for WPA supplicant network " + config.networkId + " to " + + config.SSID); + setSSIDNative(config, config.SSID); } mWifiConfigStore.enableHS20(config.isPasspoint()); @@ -1692,6 +1681,7 @@ public class WifiConfigManager { // 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added. // Thus, we had to load the FQDNs first. mConfiguredNetworks.clear(); + mScanDetailCaches.clear(); for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) { final String configKey = entry.getKey(); final WifiConfiguration config = entry.getValue(); @@ -2495,20 +2485,8 @@ public class WifiConfigManager { } private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) { + // Nothing to do if no Hotspot 2.0 provider is configured. if (!mMOManager.isConfigured()) { - if (mEnableOsuQueries) { - NetworkDetail networkDetail = scanDetail.getNetworkDetail(); - List<Constants.ANQPElementType> querySet = - ANQPFactory.buildQueryList(networkDetail, false, true); - - if (networkDetail.queriable(querySet)) { - querySet = mAnqpCache.initiate(networkDetail, querySet); - if (querySet != null) { - mSupplicantBridge.startANQP(scanDetail, querySet); - } - updateAnqpCache(scanDetail, networkDetail.getANQPElements()); - } - } return null; } NetworkDetail networkDetail = scanDetail.getNetworkDetail(); @@ -3009,11 +2987,7 @@ public class WifiConfigManager { pw.println(s); } } - if (mLocalLog != null) { - pw.println("WifiConfigManager - Log Begin ----"); - mLocalLog.dump(fd, pw, args); - pw.println("WifiConfigManager - Log End ----"); - } + if (mMOManager.isConfigured()) { pw.println("Begin dump of ANQP Cache"); mAnqpCache.dump(pw); @@ -3147,15 +3121,6 @@ public class WifiConfigManager { } /** - * Checks if the network is a sim config. - * @param config Config corresponding to the network. - * @return true if it is a sim config, false otherwise. - */ - public boolean isSimConfig(WifiConfiguration config) { - return mWifiConfigStore.isSimConfig(config); - } - - /** * Resets all sim networks from the network list. */ public void resetSimNetworks() { @@ -3269,29 +3234,6 @@ public class WifiConfigManager { } } - /** called when CS ask WiFistateMachine to disconnect the current network - * because the score is bad. - */ - void handleBadNetworkDisconnectReport(int netId, WifiInfo info) { - /* TODO verify the bad network is current */ - WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId); - if (config != null) { - if ((info.is24GHz() && info.getRssi() - <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND) - || (info.is5GHz() && info.getRssi() - <= WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND)) { - // We do not block due to bad RSSI since network selection should not select bad - // RSSI candidate - } else { - // We got disabled but RSSI is good, so disable hard - updateNetworkSelectionStatus(config, - WifiConfiguration.NetworkSelectionStatus.DISABLED_BAD_LINK); - } - } - // Record last time Connectivity Service switched us away from WiFi and onto Cell - mLastUnwantedNetworkDisconnectTimestamp = mClock.currentTimeMillis(); - } - int getMaxDhcpRetries() { return mFacade.getIntegerSetting(mContext, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index b32d0af25..908487152 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.content.Context; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; import android.net.wifi.WifiConfiguration; @@ -36,6 +37,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.server.wifi.hotspot2.Utils; +import com.android.server.wifi.util.TelephonyUtil; import org.json.JSONException; import org.json.JSONObject; @@ -92,6 +94,7 @@ public class WifiConfigStore { private final LocalLog mLocalLog; private final WpaConfigFileObserver mFileObserver; + private final Context mContext; private final WifiNative mWifiNative; private final KeyStore mKeyStore; private final boolean mShowNetworks; @@ -99,8 +102,9 @@ public class WifiConfigStore { private final BackupManagerProxy mBackupManagerProxy; - WifiConfigStore(WifiNative wifiNative, KeyStore keyStore, LocalLog localLog, + WifiConfigStore(Context context, WifiNative wifiNative, KeyStore keyStore, LocalLog localLog, boolean showNetworks, boolean verboseDebug) { + mContext = context; mWifiNative = wifiNative; mKeyStore = keyStore; mShowNetworks = showNetworks; @@ -1095,27 +1099,6 @@ public class WifiConfigStore { } /** - * Checks if the network is a sim config. - * - * @param config Config corresponding to the network. - * @return true if it is a sim config, false otherwise. - */ - public boolean isSimConfig(WifiConfiguration config) { - if (config == null) { - return false; - } - - if (config.enterpriseConfig == null) { - return false; - } - - int method = config.enterpriseConfig.getEapMethod(); - return (method == WifiEnterpriseConfig.Eap.SIM - || method == WifiEnterpriseConfig.Eap.AKA - || method == WifiEnterpriseConfig.Eap.AKA_PRIME); - } - - /** * Resets all sim networks from the provided network list. * * @param configs List of all the networks. @@ -1123,10 +1106,26 @@ public class WifiConfigStore { public void resetSimNetworks(Collection<WifiConfiguration> configs) { if (VDBG) localLog("resetSimNetworks"); for (WifiConfiguration config : configs) { - if (isSimConfig(config)) { - /* This configuration may have cached Pseudonym IDs; lets remove them */ - mWifiNative.setNetworkVariable(config.networkId, "identity", "NULL"); - mWifiNative.setNetworkVariable(config.networkId, "anonymous_identity", "NULL"); + if (TelephonyUtil.isSimConfig(config)) { + String currentIdentity = TelephonyUtil.getSimIdentity(mContext, + config.enterpriseConfig.getEapMethod()); + String supplicantIdentity = + mWifiNative.getNetworkVariable(config.networkId, "identity"); + if(supplicantIdentity != null) { + supplicantIdentity = removeDoubleQuotes(supplicantIdentity); + } + if (currentIdentity == null || !currentIdentity.equals(supplicantIdentity)) { + // Identity differs so update the identity + mWifiNative.setNetworkVariable(config.networkId, + WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.EMPTY_VALUE); + // This configuration may have cached Pseudonym IDs; lets remove them + mWifiNative.setNetworkVariable(config.networkId, + WifiEnterpriseConfig.ANON_IDENTITY_KEY, + WifiEnterpriseConfig.EMPTY_VALUE); + } + // Update the loaded config + config.enterpriseConfig.setIdentity(currentIdentity); + config.enterpriseConfig.setAnonymousIdentity(""); } } } diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index 7c0483669..1ca2608cb 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -90,7 +90,8 @@ public class WifiConnectivityManager { private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds // Maximum number of retries when starting a scan failed - private static final int MAX_SCAN_RESTART_ALLOWED = 5; + @VisibleForTesting + public static final int MAX_SCAN_RESTART_ALLOWED = 5; // Number of milli-seconds to delay before retry starting // a previously failed scan private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds @@ -132,7 +133,7 @@ public class WifiConnectivityManager { private final Handler mEventHandler; private final Clock mClock; private final LocalLog mLocalLog = - new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 1024); + new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256); private final LinkedList<Long> mConnectionAttemptTimeStamps; private boolean mDbg = false; @@ -147,6 +148,9 @@ public class WifiConnectivityManager { private String mLastConnectionAttemptBssid = null; private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS; private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; + private boolean mPnoScanStarted = false; + private boolean mPeriodicScanTimerSet = false; + private boolean mWaitForFullBandScanResults = false; // PNO settings private int mMin5GHzRssi; @@ -178,11 +182,9 @@ public class WifiConnectivityManager { // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times // if the start scan command failed. An timer is used here to make it a deferred retry. private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { - private final boolean mIsWatchdogTriggered; private boolean mIsFullBandScan; - RestartSingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) { - mIsWatchdogTriggered = isWatchdogTriggered; + RestartSingleScanListener(boolean isFullBandScan) { mIsFullBandScan = isFullBandScan; } @@ -191,7 +193,7 @@ public class WifiConnectivityManager { if (mStateMachine.getScanCount() < mStateMachine.getMaxConfiguredScanCount()) { mIsFullBandScan = false; } - startSingleScan(mIsWatchdogTriggered, mIsFullBandScan); + startSingleScan(mIsFullBandScan); } } @@ -231,6 +233,7 @@ public class WifiConnectivityManager { mStateMachine.isSupplicantTransientState()); mWifiLastResortWatchdog.updateAvailableNetworks( mQualifiedNetworkSelector.getFilteredScanDetails()); + mWifiMetrics.countScanResults(scanDetails); if (candidate != null) { localLog(listenerName + ": QNS candidate-" + candidate.SSID); connectToNetwork(candidate); @@ -252,9 +255,6 @@ public class WifiConnectivityManager { @Override public void onSuccess() { localLog("PeriodicScanListener onSuccess"); - - // reset the count - mScanRestartCount = 0; } @Override @@ -283,6 +283,7 @@ public class WifiConnectivityManager { public void onResults(WifiScanner.ScanData[] results) { handleScanResults(mScanDetails, "PeriodicScanListener"); clearScanDetails(); + mScanRestartCount = 0; } @Override @@ -299,23 +300,101 @@ public class WifiConnectivityManager { private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener(); + // All single scan results listener. + // + // Note: This is the listener for all the available single scan results, + // including the ones initiated by WifiConnectivityManager and + // other modules. + private class AllSingleScanListener implements WifiScanner.ScanListener { + private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); + + public void clearScanDetails() { + mScanDetails.clear(); + } + + @Override + public void onSuccess() { + localLog("registerScanListener onSuccess"); + } + + @Override + public void onFailure(int reason, String description) { + Log.e(TAG, "registerScanListener onFailure:" + + " reason: " + reason + + " description: " + description); + } + + @Override + public void onPeriodChanged(int periodInMs) { + } + + @Override + public void onResults(WifiScanner.ScanData[] results) { + if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { + clearScanDetails(); + mWaitForFullBandScanResults = false; + return; + } + + // Full band scan results only. + if (mWaitForFullBandScanResults) { + if (!results[0].isAllChannelsScanned()) { + localLog("AllSingleScanListener waiting for full band scan results."); + clearScanDetails(); + return; + } else { + mWaitForFullBandScanResults = false; + } + } + + boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener"); + clearScanDetails(); + + // Update metrics to see if a single scan detected a valid network + // while PNO scan didn't. + // Note: We don't update the background scan metrics any more as it is + // not in use. + if (mPnoScanStarted) { + if (wasConnectAttempted) { + mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); + } else { + mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); + } + } + } + + @Override + public void onFullResult(ScanResult fullScanResult) { + if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { + return; + } + + if (mDbg) { + localLog("AllSingleScanListener onFullResult: " + + fullScanResult.SSID + " capabilities " + + fullScanResult.capabilities); + } + + mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult)); + } + } + + private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); + // Single scan results listener. A single scan is initiated when // Disconnected/ConnectedPNO scan found a valid network and woke up - // the system, or by the watchdog timer. + // the system, or by the watchdog timer, or to form the timer based + // periodic scan. + // + // Note: This is the listener for the single scans initiated by the + // WifiConnectivityManager. private class SingleScanListener implements WifiScanner.ScanListener { - private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); - private final boolean mIsWatchdogTriggered; private final boolean mIsFullBandScan; - SingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) { - mIsWatchdogTriggered = isWatchdogTriggered; + SingleScanListener(boolean isFullBandScan) { mIsFullBandScan = isFullBandScan; } - public void clearScanDetails() { - mScanDetails.clear(); - } - @Override public void onSuccess() { localLog("SingleScanListener onSuccess"); @@ -340,7 +419,7 @@ public class WifiConnectivityManager { // reschedule the scan if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { - scheduleDelayedSingleScan(mIsWatchdogTriggered, mIsFullBandScan); + scheduleDelayedSingleScan(mIsFullBandScan); } else { mSingleScanRestartCount = 0; Log.e(TAG, "Failed to successfully start single scan for " @@ -356,35 +435,10 @@ public class WifiConnectivityManager { @Override public void onResults(WifiScanner.ScanData[] results) { - boolean wasConnectAttempted = handleScanResults(mScanDetails, "SingleScanListener"); - clearScanDetails(); - // update metrics if this was a watchdog triggered single scan - if (mIsWatchdogTriggered) { - if (wasConnectAttempted) { - if (mScreenOn) { - mWifiMetrics.incrementNumConnectivityWatchdogBackgroundBad(); - } else { - mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); - } - } else { - if (mScreenOn) { - mWifiMetrics.incrementNumConnectivityWatchdogBackgroundGood(); - } else { - mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); - } - } - } } @Override public void onFullResult(ScanResult fullScanResult) { - if (mDbg) { - localLog("SingleScanListener onFullResult: " - + fullScanResult.SSID + " capabilities " - + fullScanResult.capabilities); - } - - mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult)); } } @@ -416,9 +470,6 @@ public class WifiConnectivityManager { @Override public void onSuccess() { localLog("PnoScanListener onSuccess"); - - // reset the count - mScanRestartCount = 0; } @Override @@ -465,6 +516,7 @@ public class WifiConnectivityManager { boolean wasConnectAttempted; wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener"); clearScanDetails(); + mScanRestartCount = 0; if (!wasConnectAttempted) { // The scan results were rejected by QNS due to low RSSI values @@ -489,7 +541,7 @@ public class WifiConnectivityManager { public WifiConnectivityManager(Context context, WifiStateMachine stateMachine, WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo, WifiQualifiedNetworkSelector qualifiedNetworkSelector, - WifiInjector wifiInjector, Looper looper) { + WifiInjector wifiInjector, Looper looper, boolean enable) { mStateMachine = stateMachine; mScanner = scanner; mConfigManager = configManager; @@ -521,7 +573,13 @@ public class WifiConnectivityManager { + " secureNetworkBonus " + mSecureBonus + " initialScoreMax " + mInitialScoreMax); - Log.i(TAG, "ConnectivityScanManager initialized "); + // Register for all single scan results + mScanner.registerScanListener(mAllSingleScanListener); + + mWifiConnectivityManagerEnabled = enable; + + Log.i(TAG, "ConnectivityScanManager initialized and " + + (enable ? "enabled" : "disabled")); } /** @@ -688,7 +746,7 @@ public class WifiConnectivityManager { Log.i(TAG, "start a single scan from watchdogHandler"); scheduleWatchdogTimer(); - startSingleScan(true, true); + startSingleScan(true); } } @@ -724,7 +782,7 @@ public class WifiConnectivityManager { if (mStateMachine.getScanCount() < mStateMachine.getMaxConfiguredScanCount()) { isFullBandScan = false; } - startSingleScan(false, isFullBandScan); + startSingleScan(isFullBandScan); schedulePeriodicScanTimer(mPeriodicSingleScanInterval); // Set up the next scan interval in an exponential backoff fashion. @@ -751,7 +809,7 @@ public class WifiConnectivityManager { } // Start a single scan - private void startSingleScan(boolean isWatchdogTriggered, boolean isFullBandScan) { + private void startSingleScan(boolean isFullBandScan) { if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { return; } @@ -787,7 +845,7 @@ public class WifiConnectivityManager { // mSingleScanListener.clearScanDetails(); // mScanner.startScan(settings, mSingleScanListener, WIFI_WORK_SOURCE); SingleScanListener singleScanListener = - new SingleScanListener(isWatchdogTriggered, isFullBandScan); + new SingleScanListener(isFullBandScan); mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); } @@ -795,6 +853,12 @@ public class WifiConnectivityManager { private void startPeriodicScan(boolean scanImmediately) { mPnoScanListener.resetLowRssiNetworkRetryDelay(); + // No connectivity scan if auto roaming is disabled. + if (mWifiState == WIFI_STATE_CONNECTED + && !mConfigManager.getEnableAutoJoinWhenAssociated()) { + return; + } + // Due to b/28020168, timer based single scan will be scheduled // to provide periodic scan in an exponential backoff fashion. if (!ENABLE_BACKGROUND_SCAN) { @@ -852,6 +916,7 @@ public class WifiConnectivityManager { mPnoScanListener.clearScanDetails(); mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); + mPnoScanStarted = true; } // Start a ConnectedPNO scan when screen is off and Wifi is connected @@ -895,6 +960,16 @@ public class WifiConnectivityManager { mPnoScanListener.clearScanDetails(); mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener); + mPnoScanStarted = true; + } + + // Stop a PNO scan. This includes both DisconnectedPNO and ConnectedPNO scans. + private void stopPnoScan() { + if (mPnoScanStarted) { + mScanner.stopPnoScan(mPnoScanListener); + } + + mPnoScanStarted = false; } // Set up watchdog timer @@ -913,14 +988,23 @@ public class WifiConnectivityManager { mClock.elapsedRealtime() + intervalMs, PERIODIC_SCAN_TIMER_TAG, mPeriodicScanTimerListener, mEventHandler); + mPeriodicScanTimerSet = true; + } + + // Cancel periodic scan timer + private void cancelPeriodicScanTimer() { + if (mPeriodicScanTimerSet) { + mAlarmManager.cancel(mPeriodicScanTimerListener); + mPeriodicScanTimerSet = false; + } } // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS - private void scheduleDelayedSingleScan(boolean isWatchdogTriggered, boolean isFullBandScan) { + private void scheduleDelayedSingleScan(boolean isFullBandScan) { localLog("scheduleDelayedSingleScan"); RestartSingleScanListener restartSingleScanListener = - new RestartSingleScanListener(isWatchdogTriggered, isFullBandScan); + new RestartSingleScanListener(isFullBandScan); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mClock.elapsedRealtime() + RESTART_SCAN_DELAY_MS, RESTART_SINGLE_SCAN_TIMER_TAG, @@ -977,11 +1061,11 @@ public class WifiConnectivityManager { // Due to b/28020168, timer based single scan will be scheduled // to provide periodic scan in an exponential backoff fashion. if (!ENABLE_BACKGROUND_SCAN) { - mAlarmManager.cancel(mPeriodicScanTimerListener); + cancelPeriodicScanTimer(); } else { mScanner.stopBackgroundScan(mPeriodicScanListener); } - mScanner.stopPnoScan(mPnoScanListener); + stopPnoScan(); mScanRestartCount = 0; } @@ -1004,8 +1088,10 @@ public class WifiConnectivityManager { mWifiState = state; - // Kick off the watchdog timer if entering disconnected state + // Reset BSSID of last connection attempt and kick off + // the watchdog timer if entering disconnected state. if (mWifiState == WIFI_STATE_DISCONNECTED) { + mLastConnectionAttemptBssid = null; scheduleWatchdogTimer(); } @@ -1042,7 +1128,8 @@ public class WifiConnectivityManager { public void forceConnectivityScan() { Log.i(TAG, "forceConnectivityScan"); - startConnectivityScan(SCAN_IMMEDIATELY); + mWaitForFullBandScanResults = true; + startSingleScan(true); } /** @@ -1085,6 +1172,10 @@ public class WifiConnectivityManager { if (!mWifiEnabled) { stopConnectivityScan(); resetLastPeriodicSingleScanTimeStamp(); + mLastConnectionAttemptBssid = null; + mWaitForFullBandScanResults = false; + } else if (mWifiConnectivityManagerEnabled) { + startConnectivityScan(SCAN_IMMEDIATELY); } } @@ -1099,6 +1190,10 @@ public class WifiConnectivityManager { if (!mWifiConnectivityManagerEnabled) { stopConnectivityScan(); resetLastPeriodicSingleScanTimeStamp(); + mLastConnectionAttemptBssid = null; + mWaitForFullBandScanResults = false; + } else if (mWifiEnabled) { + startConnectivityScan(SCAN_IMMEDIATELY); } } diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index cad047a6e..b98bf14cd 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -18,6 +18,7 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_MODE_FULL; import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF; +import static android.net.wifi.WifiManager.WIFI_MODE_NO_LOCKS_HELD; import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY; import android.app.AlarmManager; @@ -42,12 +43,15 @@ import android.util.Slog; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.wifi.WifiServiceImpl.LockList; import java.io.FileDescriptor; import java.io.PrintWriter; -class WifiController extends StateMachine { +/** + * WifiController is the class used to manage on/off state of WifiStateMachine for various operating + * modes (normal, airplane, wifi hotspot, etc.). + */ +public class WifiController extends StateMachine { private static final String TAG = "WifiController"; private static boolean DBG = false; private Context mContext; @@ -93,7 +97,7 @@ class WifiController extends StateMachine { final WifiStateMachine mWifiStateMachine; private SoftApStateMachine mSoftApStateMachine = null; final WifiSettingsStore mSettingsStore; - final LockList mLocks; + private final WifiLockManager mWifiLockManager; /** * Temporary for computing UIDS that are responsible for starting WIFI. @@ -107,22 +111,26 @@ class WifiController extends StateMachine { private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; - static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; - static final int CMD_SCREEN_ON = BASE + 2; - static final int CMD_SCREEN_OFF = BASE + 3; - static final int CMD_BATTERY_CHANGED = BASE + 4; - static final int CMD_DEVICE_IDLE = BASE + 5; - static final int CMD_LOCKS_CHANGED = BASE + 6; - static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; - static final int CMD_WIFI_TOGGLED = BASE + 8; - static final int CMD_AIRPLANE_TOGGLED = BASE + 9; - static final int CMD_SET_AP = BASE + 10; - static final int CMD_DEFERRED_TOGGLE = BASE + 11; - static final int CMD_USER_PRESENT = BASE + 12; - static final int CMD_AP_START_FAILURE = BASE + 13; - static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; - static final int CMD_AP_STOPPED = BASE + 15; - static final int CMD_STA_START_FAILURE = BASE + 16; + static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; + static final int CMD_SCREEN_ON = BASE + 2; + static final int CMD_SCREEN_OFF = BASE + 3; + static final int CMD_BATTERY_CHANGED = BASE + 4; + static final int CMD_DEVICE_IDLE = BASE + 5; + static final int CMD_LOCKS_CHANGED = BASE + 6; + static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; + static final int CMD_WIFI_TOGGLED = BASE + 8; + static final int CMD_AIRPLANE_TOGGLED = BASE + 9; + static final int CMD_SET_AP = BASE + 10; + static final int CMD_DEFERRED_TOGGLE = BASE + 11; + static final int CMD_USER_PRESENT = BASE + 12; + static final int CMD_AP_START_FAILURE = BASE + 13; + static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; + static final int CMD_AP_STOPPED = BASE + 15; + static final int CMD_STA_START_FAILURE = BASE + 16; + // Command used to trigger a wifi stack restart when in active mode + static final int CMD_RESTART_WIFI = BASE + 17; + // Internal command used to complete wifi stack restart + private static final int CMD_RESTART_WIFI_CONTINUE = BASE + 18; private DefaultState mDefaultState = new DefaultState(); private StaEnabledState mStaEnabledState = new StaEnabledState(); @@ -138,14 +146,14 @@ class WifiController extends StateMachine { private NoLockHeldState mNoLockHeldState = new NoLockHeldState(); private EcmState mEcmState = new EcmState(); - WifiController(Context context, WifiStateMachine wsm, - WifiSettingsStore wss, LockList locks, Looper looper, FrameworkFacade f) { + WifiController(Context context, WifiStateMachine wsm, WifiSettingsStore wss, + WifiLockManager wifiLockManager, Looper looper, FrameworkFacade f) { super(TAG, looper); mFacade = f; mContext = context; mWifiStateMachine = wsm; mSettingsStore = wss; - mLocks = locks; + mWifiLockManager = wifiLockManager; mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); @@ -340,7 +348,7 @@ class WifiController extends StateMachine { private void updateBatteryWorkSource() { mTmpWorkSource.clear(); if (mDeviceIdle) { - mLocks.updateWorkSource(mTmpWorkSource); + mTmpWorkSource.add(mWifiLockManager.createMergedWorkSource()); } mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource); } @@ -422,6 +430,8 @@ class WifiController extends StateMachine { case CMD_AP_START_FAILURE: case CMD_AP_STOPPED: case CMD_STA_START_FAILURE: + case CMD_RESTART_WIFI: + case CMD_RESTART_WIFI_CONTINUE: break; case CMD_USER_PRESENT: mFirstUserSignOnSeen = true; @@ -502,6 +512,9 @@ class WifiController extends StateMachine { log("DEFERRED_TOGGLE handled"); sendMessage((Message)(msg.obj)); break; + case CMD_RESTART_WIFI_CONTINUE: + transitionTo(mDeviceActiveState); + break; default: return NOT_HANDLED; } @@ -779,7 +792,9 @@ class WifiController extends StateMachine { */ private State getNextWifiState() { if (mSettingsStore.getWifiSavedState() == WifiSettingsStore.WIFI_ENABLED) { - return mDeviceActiveState; + if (!mStaAndApConcurrency) { + return mDeviceActiveState; + } } if (mSettingsStore.isScanAlwaysAvailable()) { @@ -951,6 +966,10 @@ class WifiController extends StateMachine { } mFirstUserSignOnSeen = true; return HANDLED; + } else if (msg.what == CMD_RESTART_WIFI) { + deferMessage(obtainMessage(CMD_RESTART_WIFI_CONTINUE)); + transitionTo(mApStaDisabledState); + return HANDLED; } return NOT_HANDLED; } @@ -1013,26 +1032,23 @@ class WifiController extends StateMachine { } private void checkLocksAndTransitionWhenDeviceIdle() { - if (mLocks.hasLocks()) { - switch (mLocks.getStrongestLockMode()) { - case WIFI_MODE_FULL: - transitionTo(mFullLockHeldState); - break; - case WIFI_MODE_FULL_HIGH_PERF: - transitionTo(mFullHighPerfLockHeldState); - break; - case WIFI_MODE_SCAN_ONLY: + switch (mWifiLockManager.getStrongestLockMode()) { + case WIFI_MODE_NO_LOCKS_HELD: + if (mSettingsStore.isScanAlwaysAvailable()) { transitionTo(mScanOnlyLockHeldState); - break; - default: - loge("Illegal lock " + mLocks.getStrongestLockMode()); - } - } else { - if (mSettingsStore.isScanAlwaysAvailable()) { + } else { + transitionTo(mNoLockHeldState); + } + break; + case WIFI_MODE_FULL: + transitionTo(mFullLockHeldState); + break; + case WIFI_MODE_FULL_HIGH_PERF: + transitionTo(mFullHighPerfLockHeldState); + break; + case WIFI_MODE_SCAN_ONLY: transitionTo(mScanOnlyLockHeldState); - } else { - transitionTo(mNoLockHeldState); - } + break; } } diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java index d87b14f05..a6974776b 100644 --- a/service/java/com/android/server/wifi/WifiCountryCode.java +++ b/service/java/com/android/server/wifi/WifiCountryCode.java @@ -21,6 +21,9 @@ import android.util.Log; /** * Provide functions for making changes to WiFi country code. + * This Country Code is from MCC or phone default setting. This class sends Country Code + * to driver through wpa_supplicant when WifiStateMachine marks current state as ready + * using setReadyForChange(true). */ public class WifiCountryCode { private static final String TAG = "WifiCountryCode"; @@ -149,13 +152,34 @@ public class WifiCountryCode { } /** - * @return Get the current country code, returns null if no country code is set. + * Method to get the Country Code that was sent to wpa_supplicant. + * + * @return Returns the local copy of the Country Code that was sent to the driver upon + * setReadyForChange(true). + * If wpa_supplicant was never started, this may be null even if a SIM reported a valid + * country code. + * Returns null if no Country Code was sent to driver. */ public synchronized String getCurrentCountryCode() { mCurrentCountryCode = pickCountryCode(); return mCurrentCountryCode; } + public synchronized String getCountryCodeSentToDriver() { + return mCurrentCountryCode; + } + + /** + * Method to return the currently reported Country Code from the SIM or phone default setting. + * + * @return The currently reported Country Code from the SIM. If there is no Country Code + * reported from SIM, a phone default Country Code will be returned. + * Returns null when there is no Country Code available. + */ + public synchronized String getCountryCode() { + return pickCountryCode(); + } + private void updateCountryCode() { if (DBG) Log.d(TAG, "Update country code"); String country = pickCountryCode(); @@ -164,7 +188,7 @@ public class WifiCountryCode { // 1. Wpa supplicant may silently modify the country code. // 2. If Wifi restarted therefoere wpa_supplicant also restarted, // the country code counld be reset to '00' by wpa_supplicant. - if (country.length() != 0) { + if (country != null) { setCountryCodeNative(country); } // We do not set country code if there is no candidate. This is reasonable @@ -179,8 +203,8 @@ public class WifiCountryCode { if (mDefaultCountryCode != null) { return mDefaultCountryCode; } - // If there is no candidate country code we will return an empty string. - return ""; + // If there is no candidate country code we will return null. + return null; } private boolean setCountryCodeNative(String country) { diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java index 896c1c816..0885e46fd 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -80,6 +80,8 @@ public class WifiLastResortWatchdog { private WifiMetrics mWifiMetrics; + private WifiController mWifiController = null; + WifiLastResortWatchdog(WifiMetrics wifiMetrics) { mWifiMetrics = wifiMetrics; } @@ -207,6 +209,12 @@ public class WifiLastResortWatchdog { public void connectedStateTransition(boolean isEntering) { if (VDBG) Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering); mWifiIsConnected = isEntering; + + if (!mWatchdogAllowedToTrigger) { + // WiFi has connected after a Watchdog trigger, without any new networks becoming + // available, log a Watchdog success in wifi metrics + mWifiMetrics.incrementNumLastResortWatchdogSuccesses(); + } if (isEntering) { // We connected to something! Reset failure counts for everything clearAllFailureCounts(); @@ -324,13 +332,21 @@ public class WifiLastResortWatchdog { } /** - * Restart Supplicant, Driver & return WifiStateMachine to InitialState + * Trigger a restart of the wifi stack. */ private void restartWifiStack() { if (VDBG) Log.v(TAG, "restartWifiStack."); - Log.i(TAG, "Triggered."); + + // First verify that we can send the trigger message. + if (mWifiController == null) { + Log.e(TAG, "WifiLastResortWatchdog unable to trigger: WifiController is null"); + return; + } + if (DBG) Log.d(TAG, toString()); - // <TODO> + + mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI); + Log.i(TAG, "Triggered WiFi stack restart."); } /** @@ -537,4 +553,14 @@ public class WifiLastResortWatchdog { + ", Age: " + age; } } + + /** + * Method used to set the WifiController for the this watchdog. + * + * The WifiController is used to send the restart wifi command to carry out the wifi restart. + * @param wifiController WifiController instance that will be sent the CMD_RESTART_WIFI message. + */ + public void setWifiController(WifiController wifiController) { + mWifiController = wifiController; + } } diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java new file mode 100644 index 000000000..fe193f097 --- /dev/null +++ b/service/java/com/android/server/wifi/WifiLockManager.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2016 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 android.content.Context; +import android.net.wifi.WifiManager; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.WorkSource; +import android.util.Slog; + +import com.android.internal.app.IBatteryStats; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * WifiLockManager maintains the list of wake locks held by different applications. + */ +public class WifiLockManager { + private static final String TAG = "WifiLockManager"; + private boolean mVerboseLoggingEnabled = false; + + private final Context mContext; + private final IBatteryStats mBatteryStats; + + private final List<WifiLock> mWifiLocks = new ArrayList<>(); + // some wifi lock statistics + private int mFullHighPerfLocksAcquired; + private int mFullHighPerfLocksReleased; + private int mFullLocksAcquired; + private int mFullLocksReleased; + private int mScanLocksAcquired; + private int mScanLocksReleased; + + WifiLockManager(Context context, IBatteryStats batteryStats) { + mContext = context; + mBatteryStats = batteryStats; + } + + /** + * Method allowing a calling app to acquire a Wifi WakeLock in the supplied mode. + * + * This method verifies that the caller has permission to make the call and that the lock mode + * is a valid WifiLock mode. + * @param lockMode int representation of the Wifi WakeLock type. + * @param tag String passed to WifiManager.WifiLock + * @param binder IBinder for the calling app + * @param ws WorkSource of the calling app + * + * @return true if the lock was successfully acquired, false if the lockMode was invalid. + */ + public boolean acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + if (!isValidLockMode(lockMode)) { + throw new IllegalArgumentException("lockMode =" + lockMode); + } + if (ws == null || ws.size() == 0) { + ws = new WorkSource(Binder.getCallingUid()); + } else { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + } + return addLock(new WifiLock(lockMode, tag, binder, ws)); + } + + /** + * Method used by applications to release a WiFi Wake lock. This method checks permissions for + * the caller and if allowed, releases the underlying WifiLock(s). + * + * @param binder IBinder for the calling app. + * @return true if the lock was released, false if the caller did not hold any locks + */ + public boolean releaseWifiLock(IBinder binder) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + return releaseLock(binder); + } + + /** + * Method used to get the strongest lock type currently held by the WifiLockManager. + * + * If no locks are held, WifiManager.WIFI_MODE_NO_LOCKS_HELD is returned. + * + * @return int representing the currently held (highest power consumption) lock. + */ + public synchronized int getStrongestLockMode() { + if (mWifiLocks.isEmpty()) { + return WifiManager.WIFI_MODE_NO_LOCKS_HELD; + } + + if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) { + return WifiManager.WIFI_MODE_FULL_HIGH_PERF; + } + + if (mFullLocksAcquired > mFullLocksReleased) { + return WifiManager.WIFI_MODE_FULL; + } + + return WifiManager.WIFI_MODE_SCAN_ONLY; + } + + /** + * Method to create a WorkSource containing all active WifiLock WorkSources. + */ + public synchronized WorkSource createMergedWorkSource() { + WorkSource mergedWS = new WorkSource(); + for (WifiLock lock : mWifiLocks) { + mergedWS.add(lock.getWorkSource()); + } + return mergedWS; + } + + /** + * Method used to update WifiLocks with a new WorkSouce. + * + * @param binder IBinder for the calling application. + * @param ws WorkSource to add to the existing WifiLock(s). + */ + public synchronized void updateWifiLockWorkSource(IBinder binder, WorkSource ws) { + // Does the caller have permission to make this call? + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, null); + + // Now check if there is an active lock + WifiLock wl = findLockByBinder(binder); + if (wl == null) { + throw new IllegalArgumentException("Wifi lock not active"); + } + + WorkSource newWorkSource; + if (ws == null || ws.size() == 0) { + newWorkSource = new WorkSource(Binder.getCallingUid()); + } else { + // Make a copy of the WorkSource before adding it to the WakeLock + newWorkSource = new WorkSource(ws); + } + + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteFullWifiLockReleasedFromSource(wl.mWorkSource); + wl.mWorkSource = newWorkSource; + mBatteryStats.noteFullWifiLockAcquiredFromSource(wl.mWorkSource); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private static boolean isValidLockMode(int lockMode) { + if (lockMode != WifiManager.WIFI_MODE_FULL + && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY + && lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) { + return false; + } + return true; + } + + private synchronized boolean addLock(WifiLock lock) { + if (mVerboseLoggingEnabled) { + Slog.d(TAG, "addLock: " + lock); + } + + if (findLockByBinder(lock.getBinder()) != null) { + if (mVerboseLoggingEnabled) { + Slog.d(TAG, "attempted to add a lock when already holding one"); + } + return false; + } + + mWifiLocks.add(lock); + + boolean lockAdded = false; + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteFullWifiLockAcquiredFromSource(lock.mWorkSource); + switch(lock.mMode) { + case WifiManager.WIFI_MODE_FULL: + ++mFullLocksAcquired; + break; + case WifiManager.WIFI_MODE_FULL_HIGH_PERF: + ++mFullHighPerfLocksAcquired; + break; + case WifiManager.WIFI_MODE_SCAN_ONLY: + ++mScanLocksAcquired; + break; + } + lockAdded = true; + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + return lockAdded; + } + + private synchronized WifiLock removeLock(IBinder binder) { + WifiLock lock = findLockByBinder(binder); + if (lock != null) { + mWifiLocks.remove(lock); + lock.unlinkDeathRecipient(); + } + return lock; + } + + private synchronized boolean releaseLock(IBinder binder) { + WifiLock wifiLock = removeLock(binder); + if (wifiLock == null) { + // attempting to release a lock that is not active. + return false; + } + + if (mVerboseLoggingEnabled) { + Slog.d(TAG, "releaseLock: " + wifiLock); + } + + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); + switch(wifiLock.mMode) { + case WifiManager.WIFI_MODE_FULL: + ++mFullLocksReleased; + break; + case WifiManager.WIFI_MODE_FULL_HIGH_PERF: + ++mFullHighPerfLocksReleased; + break; + case WifiManager.WIFI_MODE_SCAN_ONLY: + ++mScanLocksReleased; + break; + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + return true; + } + + + private synchronized WifiLock findLockByBinder(IBinder binder) { + for (WifiLock lock : mWifiLocks) { + if (lock.getBinder() == binder) { + return lock; + } + } + return null; + } + + protected void dump(PrintWriter pw) { + pw.println("Locks acquired: " + mFullLocksAcquired + " full, " + + mFullHighPerfLocksAcquired + " full high perf, " + + mScanLocksAcquired + " scan"); + pw.println("Locks released: " + mFullLocksReleased + " full, " + + mFullHighPerfLocksReleased + " full high perf, " + + mScanLocksReleased + " scan"); + pw.println(); + pw.println("Locks held:"); + for (WifiLock lock : mWifiLocks) { + pw.print(" "); + pw.println(lock); + } + } + + protected void enableVerboseLogging(int verbose) { + if (verbose > 0) { + mVerboseLoggingEnabled = true; + } else { + mVerboseLoggingEnabled = false; + } + } + + private class WifiLock implements IBinder.DeathRecipient { + String mTag; + int mUid; + IBinder mBinder; + int mMode; + WorkSource mWorkSource; + + WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { + mTag = tag; + mBinder = binder; + mUid = Binder.getCallingUid(); + mMode = lockMode; + mWorkSource = ws; + try { + mBinder.linkToDeath(this, 0); + } catch (RemoteException e) { + binderDied(); + } + } + + protected WorkSource getWorkSource() { + return mWorkSource; + } + + protected int getUid() { + return mUid; + } + + protected IBinder getBinder() { + return mBinder; + } + + public void binderDied() { + releaseLock(mBinder); + } + + public void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } + + public String toString() { + return "WifiLock{" + this.mTag + " type=" + this.mMode + " uid=" + mUid + "}"; + } + } +} diff --git a/service/java/com/android/server/wifi/WifiLogger.java b/service/java/com/android/server/wifi/WifiLogger.java index 4251df457..c15e2a841 100644 --- a/service/java/com/android/server/wifi/WifiLogger.java +++ b/service/java/com/android/server/wifi/WifiLogger.java @@ -16,10 +16,12 @@ package com.android.server.wifi; +import android.content.Context; import android.util.Base64; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.R; import com.android.server.wifi.util.ByteArrayRingBuffer; import com.android.server.wifi.util.StringUtil; @@ -89,13 +91,13 @@ class WifiLogger extends BaseWifiLogger { /** minimum buffer size for each of the log levels */ private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 }; - @VisibleForTesting public static final int RING_BUFFER_BYTE_LIMIT_SMALL = 32 * 1024; - @VisibleForTesting public static final int RING_BUFFER_BYTE_LIMIT_LARGE = 1024 * 1024; @VisibleForTesting public static final String FIRMWARE_DUMP_SECTION_HEADER = "FW Memory dump"; @VisibleForTesting public static final String DRIVER_DUMP_SECTION_HEADER = "Driver state dump"; + private final int RING_BUFFER_BYTE_LIMIT_SMALL; + private final int RING_BUFFER_BYTE_LIMIT_LARGE; private int mLogLevel = VERBOSE_NO_LOG; private boolean mIsLoggingEventHandlerRegistered; private WifiNative.RingBufferStatus[] mRingBuffers; @@ -103,15 +105,20 @@ class WifiLogger extends BaseWifiLogger { private WifiStateMachine mWifiStateMachine; private final WifiNative mWifiNative; private final BuildProperties mBuildProperties; - private int mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL; + private int mMaxRingBufferSizeBytes; + + public WifiLogger(Context context, WifiStateMachine wifiStateMachine, WifiNative wifiNative, + BuildProperties buildProperties) { + RING_BUFFER_BYTE_LIMIT_SMALL = context.getResources().getInteger( + R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb) * 1024; + RING_BUFFER_BYTE_LIMIT_LARGE = context.getResources().getInteger( + R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb) * 1024; - public WifiLogger( - WifiStateMachine wifiStateMachine, WifiNative wifiNative, - BuildProperties buildProperties) { mWifiStateMachine = wifiStateMachine; mWifiNative = wifiNative; mBuildProperties = buildProperties; mIsLoggingEventHandlerRegistered = false; + mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL; } @Override @@ -224,8 +231,11 @@ class WifiLogger extends BaseWifiLogger { } dumpPacketFates(pw); - pw.println("--------------------------------------------------------------------"); + + pw.println("WifiNative - Log Begin ----"); + mWifiNative.getLocalLog().dump(fd, pw, args); + pw.println("WifiNative - Log End ----"); } /* private methods and data */ @@ -536,7 +546,7 @@ class WifiLogger extends BaseWifiLogger { String result; //compress Deflater compressor = new Deflater(); - compressor.setLevel(Deflater.BEST_COMPRESSION); + compressor.setLevel(Deflater.BEST_SPEED); compressor.setInput(input); compressor.finish(); ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length); diff --git a/service/java/com/android/server/wifi/WifiLoggerHal.java b/service/java/com/android/server/wifi/WifiLoggerHal.java index ce3640182..0294e9bf7 100644 --- a/service/java/com/android/server/wifi/WifiLoggerHal.java +++ b/service/java/com/android/server/wifi/WifiLoggerHal.java @@ -32,8 +32,8 @@ public class WifiLoggerHal { public static final byte TX_PKT_FATE_FW_DROP_OTHER = 5; public static final byte TX_PKT_FATE_DRV_QUEUED = 6; public static final byte TX_PKT_FATE_DRV_DROP_INVALID = 7; - public static final byte TX_PKT_FATE_DRV_DROP_NOBUFS = 9; - public static final byte TX_PKT_FATE_DRV_DROP_OTHER = 10; + public static final byte TX_PKT_FATE_DRV_DROP_NOBUFS = 8; + public static final byte TX_PKT_FATE_DRV_DROP_OTHER = 9; public static final byte RX_PKT_FATE_SUCCESS = 0; public static final byte RX_PKT_FATE_FW_QUEUED = 1; @@ -46,4 +46,9 @@ public class WifiLoggerHal { public static final byte RX_PKT_FATE_DRV_DROP_INVALID = 8; public static final byte RX_PKT_FATE_DRV_DROP_NOBUFS = 9; public static final byte RX_PKT_FATE_DRV_DROP_OTHER = 10; + + /** These aren't formally part of the HAL. But they probably should be, eventually. */ + public static final byte WIFI_ALERT_REASON_RESERVED = 0; + public static final byte WIFI_ALERT_REASON_MIN = 0; + public static final byte WIFI_ALERT_REASON_MAX = 64; } diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 855afa7e0..6cf87fef0 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.net.NetworkAgent; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.util.Base64; @@ -42,6 +43,13 @@ import java.util.List; public class WifiMetrics { private static final String TAG = "WifiMetrics"; private static final boolean DBG = false; + /** + * Clamp the RSSI poll counts to values between [MIN,MAX]_RSSI_POLL + */ + private static final int MAX_RSSI_POLL = 0; + private static final int MIN_RSSI_POLL = -127; + private static final int MIN_WIFI_SCORE = 0; + private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE; private final Object mLock = new Object(); private static final int MAX_CONNECTION_EVENTS = 256; private Clock mClock; @@ -53,11 +61,11 @@ public class WifiMetrics { * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced * together at dump-time */ - private final WifiMetricsProto.WifiLog mWifiLogProto; + private final WifiMetricsProto.WifiLog mWifiLogProto = new WifiMetricsProto.WifiLog(); /** * Session information that gets logged for every Wifi connection attempt. */ - private final List<ConnectionEvent> mConnectionEventList; + private final List<ConnectionEvent> mConnectionEventList = new ArrayList<>(); /** * The latest started (but un-ended) connection attempt */ @@ -65,18 +73,23 @@ public class WifiMetrics { /** * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode */ - private SparseIntArray mScanReturnEntries; + private final SparseIntArray mScanReturnEntries = new SparseIntArray(); /** * Mapping of system state to the counts of scans requested in that wifi state * screenOn * combination. Indexed by WifiLog.WifiState * (1 + screenOn) */ - private SparseIntArray mWifiSystemStateEntries; + private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray(); + /** Mapping of RSSI values to counts. */ + private final SparseIntArray mRssiPollCounts = new SparseIntArray(); + /** Mapping of alert reason to the respective alert count. */ + private final SparseIntArray mWifiAlertReasonCounts = new SparseIntArray(); /** * Records the elapsedRealtime (in seconds) that represents the beginning of data * capture for for this WifiMetricsProto */ private long mRecordStartTimeSec; - + /** Mapping of Wifi Scores to counts */ + private final SparseIntArray mWifiScoreCounts = new SparseIntArray(); class RouterFingerPrint { private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto; RouterFingerPrint() { @@ -121,6 +134,8 @@ public class WifiMetrics { mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL; } + mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto + .passpoint = config.isPasspoint(); // If there's a ScanResult candidate associated with this config already, get it and // log (more accurate) metrics from it ScanResult candidate = config.getNetworkSelectionStatus().getCandidate(); @@ -304,10 +319,6 @@ public class WifiMetrics { public WifiMetrics(Clock clock) { mClock = clock; - mWifiLogProto = new WifiMetricsProto.WifiLog(); - mConnectionEventList = new ArrayList<>(); - mScanReturnEntries = new SparseIntArray(); - mWifiSystemStateEntries = new SparseIntArray(); mCurrentConnectionEvent = null; mScreenOn = true; mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED; @@ -510,6 +521,18 @@ public class WifiMetrics { } } + void setNumHiddenNetworks(int num) { + synchronized (mLock) { + mWifiLogProto.numHiddenNetworks = num; + } + } + + void setNumPasspointNetworks(int num) { + synchronized (mLock) { + mWifiLogProto.numPasspointNetworks = num; + } + } + void setNumNetworksAddedByUser(int num) { synchronized (mLock) { mWifiLogProto.numNetworksAddedByUser = num; @@ -790,10 +813,119 @@ public class WifiMetrics { } } + /** + * Increment occurence count of RSSI level from RSSI poll. + * Ignores rssi values outside the bounds of [MIN_RSSI_POLL, MAX_RSSI_POLL] + */ + public void incrementRssiPollRssiCount(int rssi) { + if (!(rssi >= MIN_RSSI_POLL && rssi <= MAX_RSSI_POLL)) { + return; + } + synchronized (mLock) { + int count = mRssiPollCounts.get(rssi); + mRssiPollCounts.put(rssi, count + 1); + } + } + + /** + * Increment count of Watchdog successes. + */ + public void incrementNumLastResortWatchdogSuccesses() { + synchronized (mLock) { + mWifiLogProto.numLastResortWatchdogSuccesses++; + } + } + + /** + * Increments the count of alerts by alert reason. + * + * @param reason The cause of the alert. The reason values are driver-specific. + */ + public void incrementAlertReasonCount(int reason) { + if (reason > WifiLoggerHal.WIFI_ALERT_REASON_MAX + || reason < WifiLoggerHal.WIFI_ALERT_REASON_MIN) { + reason = WifiLoggerHal.WIFI_ALERT_REASON_RESERVED; + } + synchronized (mLock) { + int alertCount = mWifiAlertReasonCounts.get(reason); + mWifiAlertReasonCounts.put(reason, alertCount + 1); + } + } + + /** + * Counts all the different types of networks seen in a set of scan results + */ + public void countScanResults(List<ScanDetail> scanDetails) { + if (scanDetails == null) { + return; + } + int totalResults = 0; + int openNetworks = 0; + int personalNetworks = 0; + int enterpriseNetworks = 0; + int hiddenNetworks = 0; + int hotspot2r1Networks = 0; + int hotspot2r2Networks = 0; + for (ScanDetail scanDetail : scanDetails) { + NetworkDetail networkDetail = scanDetail.getNetworkDetail(); + ScanResult scanResult = scanDetail.getScanResult(); + totalResults++; + if (networkDetail != null) { + if (networkDetail.isHiddenBeaconFrame()) { + hiddenNetworks++; + } + if (networkDetail.getHSRelease() != null) { + if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) { + hotspot2r1Networks++; + } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) { + hotspot2r2Networks++; + } + } + } + if (scanResult != null && scanResult.capabilities != null) { + if (scanResult.capabilities.contains("EAP")) { + enterpriseNetworks++; + } else if (scanResult.capabilities.contains("PSK") + || scanResult.capabilities.contains("WEP")) { + personalNetworks++; + } else { + openNetworks++; + } + } + } + synchronized (mLock) { + mWifiLogProto.numTotalScanResults += totalResults; + mWifiLogProto.numOpenNetworkScanResults += openNetworks; + mWifiLogProto.numPersonalNetworkScanResults += personalNetworks; + mWifiLogProto.numEnterpriseNetworkScanResults += enterpriseNetworks; + mWifiLogProto.numHiddenNetworkScanResults += hiddenNetworks; + mWifiLogProto.numHotspot2R1NetworkScanResults += hotspot2r1Networks; + mWifiLogProto.numHotspot2R2NetworkScanResults += hotspot2r2Networks; + mWifiLogProto.numScans++; + } + } + + /** + * Increments occurence of a particular wifi score calculated + * in WifiScoreReport by current connected network. Scores are bounded + * within [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray + */ + public void incrementWifiScoreCount(int score) { + if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) { + return; + } + synchronized (mLock) { + int count = mWifiScoreCounts.get(score); + mWifiScoreCounts.put(score, count + 1); + } + } + public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; + public static final String CLEAN_DUMP_ARG = "clean"; + /** * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager - * at this time + * at this time. * * @param fd unused * @param pw PrintWriter for writing dump to @@ -814,10 +946,18 @@ public class WifiMetrics { } byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto); String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT); - pw.println(metricsProtoDump); - pw.println("EndWifiMetrics"); + if (args.length > 1 && CLEAN_DUMP_ARG.equals(args[1])) { + // Output metrics proto bytes (base64) and nothing else + pw.print(metricsProtoDump); + } else { + // Tag the start and end of the metrics proto bytes + pw.println("WifiMetrics:"); + pw.println(metricsProtoDump); + pw.println("EndWifiMetrics"); + } clear(); } else { + pw.println("WifiMetrics:"); pw.println("mConnectionEvents:"); for (ConnectionEvent event : mConnectionEventList) { String eventLine = event.toString(); @@ -832,6 +972,9 @@ public class WifiMetrics { + mWifiLogProto.numPersonalNetworks); pw.println("mWifiLogProto.numEnterpriseNetworks=" + mWifiLogProto.numEnterpriseNetworks); + pw.println("mWifiLogProto.numHiddenNetworks=" + mWifiLogProto.numHiddenNetworks); + pw.println("mWifiLogProto.numPasspointNetworks=" + + mWifiLogProto.numPasspointNetworks); pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled); pw.println("mWifiLogProto.isScanningAlwaysEnabled=" + mWifiLogProto.isScanningAlwaysEnabled); @@ -905,8 +1048,53 @@ public class WifiMetrics { + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp); pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther=" + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther); + pw.println("mWifiLogProto.numLastResortWatchdogSuccesses=" + + mWifiLogProto.numLastResortWatchdogSuccesses); pw.println("mWifiLogProto.recordDurationSec=" + ((mClock.elapsedRealtime() / 1000) - mRecordStartTimeSec)); + pw.println("mWifiLogProto.rssiPollRssiCount: Printing counts for [" + MIN_RSSI_POLL + + ", " + MAX_RSSI_POLL + "]"); + StringBuilder sb = new StringBuilder(); + for (int i = MIN_RSSI_POLL; i <= MAX_RSSI_POLL; i++) { + sb.append(mRssiPollCounts.get(i) + " "); + } + pw.println(" " + sb.toString()); + pw.print("mWifiLogProto.alertReasonCounts="); + sb.setLength(0); + for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN; + i <= WifiLoggerHal.WIFI_ALERT_REASON_MAX; i++) { + int count = mWifiAlertReasonCounts.get(i); + if (count > 0) { + sb.append("(" + i + "," + count + "),"); + } + } + if (sb.length() > 1) { + sb.setLength(sb.length() - 1); // strip trailing comma + pw.println(sb.toString()); + } else { + pw.println("()"); + } + pw.println("mWifiLogProto.numTotalScanResults=" + + mWifiLogProto.numTotalScanResults); + pw.println("mWifiLogProto.numOpenNetworkScanResults=" + + mWifiLogProto.numOpenNetworkScanResults); + pw.println("mWifiLogProto.numPersonalNetworkScanResults=" + + mWifiLogProto.numPersonalNetworkScanResults); + pw.println("mWifiLogProto.numEnterpriseNetworkScanResults=" + + mWifiLogProto.numEnterpriseNetworkScanResults); + pw.println("mWifiLogProto.numHiddenNetworkScanResults=" + + mWifiLogProto.numHiddenNetworkScanResults); + pw.println("mWifiLogProto.numHotspot2R1NetworkScanResults=" + + mWifiLogProto.numHotspot2R1NetworkScanResults); + pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults=" + + mWifiLogProto.numHotspot2R2NetworkScanResults); + pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans); + pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", " + + MAX_WIFI_SCORE + "]"); + for (int i = 0; i <= MAX_WIFI_SCORE; i++) { + pw.print(mWifiScoreCounts.get(i) + " "); + } + pw.print("\n"); } } } @@ -919,6 +1107,9 @@ public class WifiMetrics { */ private void consolidateProto(boolean incremental) { List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>(); + List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>(); + List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>(); + List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>(); synchronized (mLock) { for (ConnectionEvent event : mConnectionEventList) { // If this is not incremental, dump full ConnectionEvent list @@ -964,6 +1155,41 @@ public class WifiMetrics { } mWifiLogProto.recordDurationSec = (int) ((mClock.elapsedRealtime() / 1000) - mRecordStartTimeSec); + + /** + * Convert the SparseIntArray of RSSI poll rssi's and counts to the proto's repeated + * IntKeyVal array. + */ + for (int i = 0; i < mRssiPollCounts.size(); i++) { + WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount(); + keyVal.rssi = mRssiPollCounts.keyAt(i); + keyVal.count = mRssiPollCounts.valueAt(i); + rssis.add(keyVal); + } + mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount); + + /** + * Convert the SparseIntArray of alert reasons and counts to the proto's repeated + * IntKeyVal array. + */ + for (int i = 0; i < mWifiAlertReasonCounts.size(); i++) { + WifiMetricsProto.AlertReasonCount keyVal = new WifiMetricsProto.AlertReasonCount(); + keyVal.reason = mWifiAlertReasonCounts.keyAt(i); + keyVal.count = mWifiAlertReasonCounts.valueAt(i); + alertReasons.add(keyVal); + } + mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount); + /** + * Convert the SparseIntArray of Wifi Score and counts to proto's repeated + * IntKeyVal array. + */ + for (int score = 0; score < mWifiScoreCounts.size(); score++) { + WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount(); + keyVal.score = mWifiScoreCounts.keyAt(score); + keyVal.count = mWifiScoreCounts.valueAt(score); + scores.add(keyVal); + } + mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount); } } @@ -979,6 +1205,9 @@ public class WifiMetrics { mScanReturnEntries.clear(); mWifiSystemStateEntries.clear(); mRecordStartTimeSec = mClock.elapsedRealtime() / 1000; + mRssiPollCounts.clear(); + mWifiAlertReasonCounts.clear(); + mWifiScoreCounts.clear(); mWifiLogProto.clear(); } } diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 8aad480e7..d9366e766 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -612,7 +612,7 @@ public class WifiMonitor { while (true) { if (mWifiNative.connectToSupplicant()) { mConnected = true; - new MonitorThread().start(); + new MonitorThread(mWifiNative.getLocalLog()).start(); return true; } if (connectTries++ < 50) { @@ -741,10 +741,11 @@ public class WifiMonitor { } private class MonitorThread extends Thread { - private final LocalLog mLocalLog = mWifiNative.getLocalLog(); + private final LocalLog mLocalLog; - public MonitorThread() { + public MonitorThread(LocalLog localLog) { super("WifiMonitor"); + mLocalLog = localLog; } public void run() { diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 07f3abfa6..f9a1b0173 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; @@ -77,6 +78,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TimeZone; @@ -104,7 +106,7 @@ public class WifiNative { private static final LocalLog sLocalLog = new LocalLog(8192); - public static LocalLog getLocalLog() { + public @NonNull LocalLog getLocalLog() { return sLocalLog; } @@ -1713,6 +1715,19 @@ public class WifiNative { public int priority; public byte flags; public byte auth_bit_field; + + @Override + public boolean equals(Object otherObj) { + if (this == otherObj) { + return true; + } else if (otherObj == null || getClass() != otherObj.getClass()) { + return false; + } + PnoNetwork other = (PnoNetwork) otherObj; + return ((Objects.equals(ssid, other.ssid)) && (networkId == other.networkId) + && (priority == other.priority) && (flags == other.flags) + && (auth_bit_field == other.auth_bit_field)); + } } /** @@ -2190,13 +2205,12 @@ public class WifiNative { synchronized (sLock) { if (isHalStarted()) { if (sRttCmdId != 0) { - Log.v("TAG", "Last one is still under measurement!"); + Log.w(TAG, "Last one is still under measurement!"); return false; } else { sRttCmdId = getNewCmdIdLocked(); } sRttEventHandler = handler; - Log.v(TAG, "native issue RTT request"); return requestRangeNative(sWlan0Index, sRttCmdId, params); } else { return false; @@ -2215,7 +2229,6 @@ public class WifiNative { if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) { sRttEventHandler = null; - Log.v(TAG, "RTT cancel Request Successfully"); return true; } else { Log.e(TAG, "RTT cancel Request failed"); diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java index e370a11a9..5b285f5f3 100644 --- a/service/java/com/android/server/wifi/WifiNetworkHistory.java +++ b/service/java/com/android/server/wifi/WifiNetworkHistory.java @@ -37,6 +37,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.text.DateFormat; import java.util.BitSet; @@ -556,12 +557,14 @@ public class WifiNetworkHistory { } } } - } catch (NumberFormatException e) { - Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e); } catch (EOFException e) { // do nothing + } catch (FileNotFoundException e) { + Log.i(TAG, "readNetworkHistory: no config file, " + e); + } catch (NumberFormatException e) { + Log.e(TAG, "readNetworkHistory: failed to parse, " + e, e); } catch (IOException e) { - Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e); + Log.e(TAG, "readNetworkHistory: failed to read, " + e, e); } } diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java index e916c8825..ed4a0bd12 100644 --- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java @@ -99,8 +99,7 @@ public class WifiQualifiedNetworkSelector { private static final int INVALID_TIME_STAMP = -1; private long mLastQualifiedNetworkSelectionTimeStamp = INVALID_TIME_STAMP; - // Temporarily, for dog food - private final LocalLog mLocalLog = new LocalLog(1024); + private final LocalLog mLocalLog = new LocalLog(512); private int mRssiScoreSlope = RSSI_SCORE_SLOPE; private int mRssiScoreOffset = RSSI_SCORE_OFFSET; private int mSameBssidAward = SAME_BSSID_AWARD; @@ -627,9 +626,9 @@ public class WifiQualifiedNetworkSelector { mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId()); } - if (mCurrentBssid == null) { - mCurrentBssid = mWifiInfo.getBSSID(); - } + // Always get the current BSSID from WifiInfo in case that firmware initiated roaming + // happened. + mCurrentBssid = mWifiInfo.getBSSID(); if (!forceSelectNetwork && !needQualifiedNetworkSelection(isLinkDebouncing, isConnected, isDisconnected, isSupplicantTransient)) { @@ -791,7 +790,9 @@ public class WifiQualifiedNetworkSelector { potentialCandidate = network; } //update the cached candidate - if (score > status.getCandidateScore()) { + if (score > status.getCandidateScore() || (score == status.getCandidateScore() + && status.getCandidate() != null + && scanResult.level > status.getCandidate().level)) { status.setCandidate(scanResult); status.setCandidateScore(score); } @@ -805,6 +806,7 @@ public class WifiQualifiedNetworkSelector { currentHighestScore = highestScore; scanResultCandidate = scanResult; networkCandidate = configurationCandidateForThisScan; + networkCandidate.getNetworkSelectionStatus().setCandidate(scanResultCandidate); } } diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java index 50e28bf28..d32c72299 100644 --- a/service/java/com/android/server/wifi/WifiScoreReport.java +++ b/service/java/com/android/server/wifi/WifiScoreReport.java @@ -97,7 +97,8 @@ public class WifiScoreReport { WifiConfigManager wifiConfigManager, NetworkAgent networkAgent, WifiScoreReport lastReport, - int aggressiveHandover) { + int aggressiveHandover, + WifiMetrics wifiMetrics) { boolean debugLogging = false; if (wifiConfigManager.mEnableVerboseLogging.get() > 0) { debugLogging = true; @@ -370,6 +371,7 @@ public class WifiScoreReport { networkAgent.sendNetworkScore(score); } } + wifiMetrics.incrementWifiScoreCount(score); return new WifiScoreReport(sb.toString(), badLinkspeedcount); } } diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 297c29f78..e5a537dd0 100755 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -136,6 +136,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { private static final boolean DBG = true; private static final boolean VDBG = false; private boolean mIsFactoryResetOn = false; + private boolean mSubSystemRestart = false; private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode"; final WifiStateMachine mWifiStateMachine; @@ -143,14 +144,6 @@ public class WifiServiceImpl extends IWifiManager.Stub { private final Context mContext; private final FrameworkFacade mFacade; - final LockList mLocks = new LockList(); - // some wifi lock statistics - private int mFullHighPerfLocksAcquired; - private int mFullHighPerfLocksReleased; - private int mFullLocksAcquired; - private int mFullLocksReleased; - private int mScanLocksAcquired; - private int mScanLocksReleased; private SoftApStateMachine mSoftApStateMachine; private int mStaAndApConcurrency = 0; private String mSoftApInterfaceName = null; @@ -343,6 +336,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { WifiStateMachineHandler mWifiStateMachineHandler; private WifiController mWifiController; + private final WifiLockManager mWifiLockManager; public WifiServiceImpl(Context context) { mContext = context; @@ -375,10 +369,11 @@ public class WifiServiceImpl extends IWifiManager.Stub { mNotificationController = new WifiNotificationController(mContext, wifiThread.getLooper(), mWifiStateMachine, mFacade, null); + mWifiLockManager = new WifiLockManager(mContext, mBatteryStats); mClientHandler = new ClientHandler(wifiThread.getLooper()); mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); mWifiController = new WifiController(mContext, mWifiStateMachine, - mSettingsStore, mLocks, wifiThread.getLooper(), mFacade); + mSettingsStore, mWifiLockManager, wifiThread.getLooper(), mFacade); if (ensureConcurrencyFileExist()) { readConcurrencyConfig(); } @@ -393,6 +388,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { } mWifiController.setSoftApStateMachine(mSoftApStateMachine); } + // Set the WifiController for WifiLastResortWatchdog + mWifiInjector.getWifiLastResortWatchdog().setWifiController(mWifiController); } @@ -431,9 +428,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) { Log.d(TAG, "resetting networks because SIM was removed"); - mWifiStateMachine.resetSimAuthNetworks(); + mWifiStateMachine.resetSimAuthNetworks(false); Log.d(TAG, "resetting country code because SIM is removed"); mCountryCode.simCardRemoved(); + } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) { + Log.d(TAG, "resetting networks because SIM was loaded"); + mWifiStateMachine.resetSimAuthNetworks(true); } } }, @@ -1187,11 +1187,13 @@ public class WifiServiceImpl extends IWifiManager.Stub { /** * Get the country code - * @return ISO 3166 country code. + * @return Get the best choice country code for wifi, regardless of if it was set or + * not. + * Returns null when there is no country code available. */ public String getCountryCode() { enforceConnectivityInternalPermission(); - String country = mCountryCode.getCurrentCountryCode(); + String country = mCountryCode.getCountryCode(); return country; } /** @@ -1449,7 +1451,38 @@ public class WifiServiceImpl extends IWifiManager.Stub { resetWifiNetworks(); mIsFactoryResetOn = false; } + if (mSubSystemRestart) { + setWifiApEnabled(null, true); + } + } else if ( state == WifiManager.WIFI_STATE_DISABLED) { + if (mSubSystemRestart) { + setWifiEnabled(true); + } } + } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { + int wifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, + WifiManager.WIFI_AP_STATE_FAILED); + if (mSubSystemRestart) { + if (wifiApState == WifiManager.WIFI_AP_STATE_DISABLED) { + if (getWifiEnabledState() == WifiManager.WIFI_STATE_ENABLED) { + setWifiEnabled(false); + } else { + /** + * STA in DISABLED state, hence just restart SAP. + * This should cover two scenarios + * 1. Only SAP ON ( before SSR ) in STA + SAP. + * 2. No STA + SAP. + */ + setWifiApEnabled(null, true); + mSubSystemRestart = false; + } + } else if (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED) { + mSubSystemRestart = false; + } + } + + } else if (action.equals(WifiManager.WIFI_AP_SUB_SYSTEM_RESTART)) { + handleSubSystemRestart(); } } }; @@ -1482,7 +1515,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - + intentFilter.addAction(WifiManager.WIFI_AP_SUB_SYSTEM_RESTART); + intentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); boolean trackEmergencyCallState = mContext.getResources().getBoolean( com.android.internal.R.bool.config_wifi_turn_off_during_emergency_call); if (trackEmergencyCallState) { @@ -1581,16 +1615,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } pw.println(); - pw.println("Locks acquired: " + mFullLocksAcquired + " full, " + - mFullHighPerfLocksAcquired + " full high perf, " + - mScanLocksAcquired + " scan"); - pw.println("Locks released: " + mFullLocksReleased + " full, " + - mFullHighPerfLocksReleased + " full high perf, " + - mScanLocksReleased + " scan"); - pw.println(); pw.println("Locks held:"); - mLocks.dump(pw); - + mWifiLockManager.dump(pw); + pw.println(); pw.println("Multicast Locks held:"); for (Multicaster l : mMulticasters) { pw.print(" "); @@ -1603,251 +1630,35 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } - private class WifiLock extends DeathRecipient { - int mMode; - WorkSource mWorkSource; - - WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { - super(tag, binder); - mMode = lockMode; - mWorkSource = ws; - } - - public void binderDied() { - synchronized (mLocks) { - releaseWifiLockLocked(mBinder); - } - } - - public String toString() { - return "WifiLock{" + mTag + " type=" + mMode + " uid=" + mUid + "}"; - } - } - - public class LockList { - private List<WifiLock> mList; - - private LockList() { - mList = new ArrayList<WifiLock>(); - } - - synchronized boolean hasLocks() { - return !mList.isEmpty(); - } - - synchronized int getStrongestLockMode() { - if (mList.isEmpty()) { - return WifiManager.WIFI_MODE_FULL; - } - - if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) { - return WifiManager.WIFI_MODE_FULL_HIGH_PERF; - } - - if (mFullLocksAcquired > mFullLocksReleased) { - return WifiManager.WIFI_MODE_FULL; - } - - return WifiManager.WIFI_MODE_SCAN_ONLY; - } - - synchronized void updateWorkSource(WorkSource ws) { - for (int i = 0; i < mLocks.mList.size(); i++) { - ws.add(mLocks.mList.get(i).mWorkSource); - } - } - - private void addLock(WifiLock lock) { - if (findLockByBinder(lock.mBinder) < 0) { - mList.add(lock); - } - } - - private WifiLock removeLock(IBinder binder) { - int index = findLockByBinder(binder); - if (index >= 0) { - WifiLock ret = mList.remove(index); - ret.unlinkDeathRecipient(); - return ret; - } else { - return null; - } - } - - private int findLockByBinder(IBinder binder) { - int size = mList.size(); - for (int i = size - 1; i >= 0; i--) { - if (mList.get(i).mBinder == binder) - return i; - } - return -1; - } - - private void dump(PrintWriter pw) { - for (WifiLock l : mList) { - pw.print(" "); - pw.println(l); - } - } - } - - void enforceWakeSourcePermission(int uid, int pid) { - if (uid == android.os.Process.myUid()) { - return; - } - mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, - pid, uid, null); - } - + @Override public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - if (lockMode != WifiManager.WIFI_MODE_FULL && - lockMode != WifiManager.WIFI_MODE_SCAN_ONLY && - lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) { - Slog.e(TAG, "Illegal argument, lockMode= " + lockMode); - if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode); - return false; - } - if (ws != null && ws.size() == 0) { - ws = null; - } - if (ws != null) { - enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid()); - } - if (ws == null) { - ws = new WorkSource(Binder.getCallingUid()); - } - WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws); - synchronized (mLocks) { - return acquireWifiLockLocked(wifiLock); - } - } - - private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException { - switch(wifiLock.mMode) { - case WifiManager.WIFI_MODE_FULL: - case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - case WifiManager.WIFI_MODE_SCAN_ONLY: - mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); - break; - } - } - - private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException { - switch(wifiLock.mMode) { - case WifiManager.WIFI_MODE_FULL: - case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - case WifiManager.WIFI_MODE_SCAN_ONLY: - mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); - break; - } - } - - private boolean acquireWifiLockLocked(WifiLock wifiLock) { - if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); - - mLocks.addLock(wifiLock); - - long ident = Binder.clearCallingIdentity(); - try { - noteAcquireWifiLock(wifiLock); - switch(wifiLock.mMode) { - case WifiManager.WIFI_MODE_FULL: - ++mFullLocksAcquired; - break; - case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - ++mFullHighPerfLocksAcquired; - break; - - case WifiManager.WIFI_MODE_SCAN_ONLY: - ++mScanLocksAcquired; - break; - } + if (mWifiLockManager.acquireWifiLock(lockMode, tag, binder, ws)) { mWifiController.sendMessage(CMD_LOCKS_CHANGED); return true; - } catch (RemoteException e) { - return false; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - if (ws != null && ws.size() == 0) { - ws = null; - } - if (ws != null) { - enforceWakeSourcePermission(uid, pid); - } - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLocks) { - int index = mLocks.findLockByBinder(lock); - if (index < 0) { - throw new IllegalArgumentException("Wifi lock not active"); - } - WifiLock wl = mLocks.mList.get(index); - noteReleaseWifiLock(wl); - wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid); - noteAcquireWifiLock(wl); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); } + return false; } - public boolean releaseWifiLock(IBinder lock) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); - synchronized (mLocks) { - return releaseWifiLockLocked(lock); - } + @Override + public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) { + mWifiLockManager.updateWifiLockWorkSource(binder, ws); } - private boolean releaseWifiLockLocked(IBinder lock) { - boolean hadLock; - - WifiLock wifiLock = mLocks.removeLock(lock); - - if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); - - hadLock = (wifiLock != null); - - long ident = Binder.clearCallingIdentity(); - try { - if (hadLock) { - noteReleaseWifiLock(wifiLock); - switch(wifiLock.mMode) { - case WifiManager.WIFI_MODE_FULL: - ++mFullLocksReleased; - break; - case WifiManager.WIFI_MODE_FULL_HIGH_PERF: - ++mFullHighPerfLocksReleased; - break; - case WifiManager.WIFI_MODE_SCAN_ONLY: - ++mScanLocksReleased; - break; - } - mWifiController.sendMessage(CMD_LOCKS_CHANGED); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); + @Override + public boolean releaseWifiLock(IBinder binder) { + if (mWifiLockManager.releaseWifiLock(binder)) { + mWifiController.sendMessage(CMD_LOCKS_CHANGED); + return true; } - - return hadLock; + return false; } - private abstract class DeathRecipient - implements IBinder.DeathRecipient { + private class Multicaster implements IBinder.DeathRecipient { String mTag; int mUid; IBinder mBinder; - DeathRecipient(String tag, IBinder binder) { - super(); + Multicaster(String tag, IBinder binder) { mTag = tag; mUid = Binder.getCallingUid(); mBinder = binder; @@ -1858,20 +1669,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } - void unlinkDeathRecipient() { - mBinder.unlinkToDeath(this, 0); - } - - public int getUid() { - return mUid; - } - } - - private class Multicaster extends DeathRecipient { - Multicaster(String tag, IBinder binder) { - super(tag, binder); - } - + @Override public void binderDied() { Slog.e(TAG, "Multicaster binderDied"); synchronized (mMulticasters) { @@ -1882,6 +1680,14 @@ public class WifiServiceImpl extends IWifiManager.Stub { } } + void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } + + public int getUid() { + return mUid; + } + public String toString() { return "Multicaster{" + mTag + " uid=" + mUid + "}"; } @@ -1971,6 +1777,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { enforceAccessPermission(); mWifiStateMachine.enableVerboseLogging(verbose); mWifiController.enableVerboseLogging(verbose); + mWifiLockManager.enableVerboseLogging(verbose); } public int getVerboseLoggingLevel() { @@ -2297,4 +2104,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { } return true; } + + private void handleSubSystemRestart() { + mSubSystemRestart = true; + setWifiApEnabled(null, false); + } } diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index 5815f0e59..f35a92b60 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -98,6 +98,7 @@ import android.util.SparseArray; import android.os.SystemProperties; 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; @@ -110,6 +111,7 @@ import com.android.server.wifi.hotspot2.IconEvent; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.hotspot2.Utils; import com.android.server.wifi.p2p.WifiP2pServiceImpl; +import com.android.server.wifi.util.TelephonyUtil; import java.io.BufferedReader; import java.io.FileDescriptor; @@ -582,10 +584,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss private WifiScanner mWifiScanner; - private int mConnectionRequests = 0; + @GuardedBy("mWifiReqCountLock") + private int mConnectionReqCount = 0; private WifiNetworkFactory mNetworkFactory; + @GuardedBy("mWifiReqCountLock") + private int mUntrustedReqCount = 0; private UntrustedWifiNetworkFactory mUntrustedNetworkFactory; private WifiNetworkAgent mNetworkAgent; + private final Object mWifiReqCountLock = new Object(); private String[] mWhiteListedSsids = null; @@ -1080,7 +1086,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss R.bool.config_wifi_enable_wifi_firmware_debugging); if (enableFirmwareLogs) { - mWifiLogger = facade.makeRealLogger(this, mWifiNative, mBuildProperties); + mWifiLogger = facade.makeRealLogger(mContext, this, mWifiNative, mBuildProperties); } else { mWifiLogger = facade.makeBaseLogger(); } @@ -1176,7 +1182,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss sendMessage(CMD_BOOT_COMPLETED); } }, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED)); mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( Settings.Global.WIFI_AUTO_CONNECT_TYPE), false, @@ -1356,25 +1362,39 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } void enableVerboseLogging(int verbose) { + if (mVerboseLoggingLevel == verbose) { + // We are already at the desired verbosity, avoid resetting StateMachine log records by + // returning here until underlying bug is fixed (b/28027593) + return; + } mVerboseLoggingLevel = verbose; mFacade.setIntegerSetting( mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, verbose); updateLoggingLevel(); } + /** + * Set wpa_supplicant log level using |mVerboseLoggingLevel| flag. + */ + void setSupplicantLogLevel() { + if (mVerboseLoggingLevel > 0) { + mWifiNative.setSupplicantLogLevel("DEBUG"); + } else { + mWifiNative.setSupplicantLogLevel("INFO"); + } + } + void updateLoggingLevel() { if (mVerboseLoggingLevel > 0) { DBG = true; - mWifiNative.setSupplicantLogLevel("DEBUG"); setLogRecSize(ActivityManager.isLowRamDeviceStatic() ? NUM_LOG_RECS_VERBOSE_LOW_MEMORY : NUM_LOG_RECS_VERBOSE); - configureVerboseHalLogging(true); } else { DBG = false; - mWifiNative.setSupplicantLogLevel("INFO"); setLogRecSize(NUM_LOG_RECS_NORMAL); - configureVerboseHalLogging(false); } + configureVerboseHalLogging(mVerboseLoggingLevel > 0); + setSupplicantLogLevel(); mCountryCode.enableVerboseLogging(mVerboseLoggingLevel); mWifiLogger.startLogging(DBG); mWifiMonitor.enableVerboseLogging(mVerboseLoggingLevel); @@ -2006,6 +2026,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } /** + * Allow tests to confirm the operational mode for WSM. + */ + @VisibleForTesting + protected int getOperationalModeForTest() { + return mOperationalMode; + } + + /** * TODO: doc */ public List<ScanResult> syncGetScanResultsList() { @@ -2280,8 +2308,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * reset cached SIM credential data */ - public synchronized void resetSimAuthNetworks() { - sendMessage(CMD_RESET_SIM_NETWORKS); + public synchronized void resetSimAuthNetworks(boolean simPresent) { + sendMessage(CMD_RESET_SIM_NETWORKS, simPresent ? 1 : 0); } /** @@ -2414,6 +2442,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (args.length > 1 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0]) + && WifiMetrics.CLEAN_DUMP_ARG.equals(args[1])) { + // Dump only wifi metrics serialized proto bytes (base64) + updateWifiMetrics(); + mWifiMetrics.dump(fd, pw, args); + return; + } super.dump(fd, pw, args); mSupplicantStateTracker.dump(fd, pw, args); pw.println("mLinkProperties " + mLinkProperties); @@ -2427,10 +2462,15 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt); pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled); pw.println("Supplicant status " + mWifiNative.status(true)); - if (mCountryCode.getCurrentCountryCode() != null) { - pw.println("CurrentCountryCode " + mCountryCode.getCurrentCountryCode()); + if (mCountryCode.getCountryCodeSentToDriver() != null) { + pw.println("CountryCode sent to driver " + mCountryCode.getCountryCodeSentToDriver()); } else { - pw.println("CurrentCountryCode is not initialized"); + if (mCountryCode.getCountryCode() != null) { + pw.println("CountryCode: " + + mCountryCode.getCountryCode() + " was not sent to driver"); + } else { + pw.println("CountryCode was not initialized"); + } } pw.println("mConnectedModeGScanOffloadStarted " + mConnectedModeGScanOffloadStarted); pw.println("mGScanPeriodMilli " + mGScanPeriodMilli); @@ -2657,7 +2697,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss sb.append(" known=").append(mNumScanResultsKnown); sb.append(" got=").append(mNumScanResultsReturned); sb.append(String.format(" bcn=%d", mRunningBeaconCount)); - sb.append(String.format(" con=%d", mConnectionRequests)); + sb.append(String.format(" con=%d", mConnectionReqCount)); + sb.append(String.format(" untrustedcn=%d", mUntrustedReqCount)); key = mWifiConfigManager.getLastSelectedConfiguration(); if (key != null) { sb.append(" last=").append(key); @@ -3030,12 +3071,16 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } enableRssiPolling(screenOn); if (mUserWantsSuspendOpt.get()) { + int shouldReleaseWakeLock = 0; if (screenOn) { - sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0); + sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, shouldReleaseWakeLock); } else { - // Allow 2s for suspend optimizations to be set - mSuspendWakeLock.acquire(2000); - sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0); + if (isConnected()) { + // Allow 2s for suspend optimizations to be set + mSuspendWakeLock.acquire(2000); + shouldReleaseWakeLock = 1; + } + sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, shouldReleaseWakeLock); } } mScreenBroadcastReceived.set(true); @@ -3281,6 +3326,10 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (newRssi > 0) newRssi -= 256; mWifiInfo.setRssi(newRssi); /* + * Log the rssi poll value in metrics + */ + mWifiMetrics.incrementRssiPollRssiCount(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 @@ -4016,23 +4065,33 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss @Override protected void needNetworkFor(NetworkRequest networkRequest, int score) { - ++mConnectionRequests; + synchronized (mWifiReqCountLock) { + if (++mConnectionReqCount == 1) { + if (mWifiConnectivityManager != null && mUntrustedReqCount == 0) { + mWifiConnectivityManager.enable(true); + } + } + } } @Override protected void releaseNetworkFor(NetworkRequest networkRequest) { - --mConnectionRequests; + synchronized (mWifiReqCountLock) { + if (--mConnectionReqCount == 0) { + if (mWifiConnectivityManager != null && mUntrustedReqCount == 0) { + mWifiConnectivityManager.enable(false); + } + } + } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("mConnectionRequests " + mConnectionRequests); + pw.println("mConnectionReqCount " + mConnectionReqCount); } } private class UntrustedWifiNetworkFactory extends NetworkFactory { - private int mUntrustedReqCount; - public UntrustedWifiNetworkFactory(Looper l, Context c, String tag, NetworkCapabilities f) { super(l, c, tag, f); } @@ -4041,9 +4100,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss protected void needNetworkFor(NetworkRequest networkRequest, int score) { if (!networkRequest.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_TRUSTED)) { - if (++mUntrustedReqCount == 1) { - if (mWifiConnectivityManager != null) { - mWifiConnectivityManager.setUntrustedConnectionAllowed(true); + synchronized (mWifiReqCountLock) { + if (++mUntrustedReqCount == 1) { + if (mWifiConnectivityManager != null) { + if (mConnectionReqCount == 0) { + mWifiConnectivityManager.enable(true); + } + mWifiConnectivityManager.setUntrustedConnectionAllowed(true); + } } } } @@ -4053,9 +4117,14 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss protected void releaseNetworkFor(NetworkRequest networkRequest) { if (!networkRequest.networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_TRUSTED)) { - if (--mUntrustedReqCount == 0) { - if (mWifiConnectivityManager != null) { - mWifiConnectivityManager.setUntrustedConnectionAllowed(false); + synchronized (mWifiReqCountLock) { + if (--mUntrustedReqCount == 0) { + if (mWifiConnectivityManager != null) { + mWifiConnectivityManager.setUntrustedConnectionAllowed(false); + if (mConnectionReqCount == 0) { + mWifiConnectivityManager.enable(false); + } + } } } } @@ -4213,7 +4282,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss break; case CMD_SET_SUSPEND_OPT_ENABLED: if (message.arg1 == 1) { - mSuspendWakeLock.release(); + if (message.arg2 == 1) { + mSuspendWakeLock.release(); + } setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true); } else { setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false); @@ -4259,7 +4330,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_FIRMWARE_ALERT: if (mWifiLogger != null) { byte[] buffer = (byte[])message.obj; - mWifiLogger.captureAlertData(message.arg1, buffer); + int alertReason = message.arg1; + mWifiLogger.captureAlertData(alertReason, buffer); + mWifiMetrics.incrementAlertReasonCount(alertReason); } break; case CMD_GET_LINK_LAYER_STATS: @@ -4424,6 +4497,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss updateDataInterface(); if (mWifiNative.startSupplicant(mP2pSupported)) { + setSupplicantLogLevel(); setWifiState(WIFI_STATE_ENABLING); if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring(mInterfaceName); @@ -4450,6 +4524,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss transitionTo(mInitialState); } break; + case CMD_SET_OPERATIONAL_MODE: + mOperationalMode = message.arg1; + break; default: return NOT_HANDLED; } @@ -4652,7 +4729,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss replyToMessage(message, message.what, stats); break; case CMD_RESET_SIM_NETWORKS: - log("resetting EAP-SIM/AKA/AKA' networks since SIM was removed"); + log("resetting EAP-SIM/AKA/AKA' networks since SIM was changed"); mWifiConfigManager.resetSimNetworks(); break; default: @@ -4811,10 +4888,13 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss if (mWifiScanner == null) { mWifiScanner = mFacade.makeWifiScanner(mContext, getHandler().getLooper()); - mWifiConnectivityManager = new WifiConnectivityManager(mContext, - WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo, - mWifiQualifiedNetworkSelector, mWifiInjector, - getHandler().getLooper()); + synchronized (mWifiReqCountLock) { + mWifiConnectivityManager = new WifiConnectivityManager(mContext, + WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo, + mWifiQualifiedNetworkSelector, mWifiInjector, + getHandler().getLooper(), hasConnectionRequests()); + mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0); + } } mWifiLogger.startLogging(DBG); @@ -4957,7 +5037,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_SET_SUSPEND_OPT_ENABLED: if (message.arg1 == 1) { setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true); - mSuspendWakeLock.release(); + if (message.arg2 == 1) { + mSuspendWakeLock.release(); + } } else { setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false); } @@ -5704,8 +5786,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss config, disableOthers, message.sendingUid); if (!ok) { messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - } else if (disableOthers) { - mTargetNetworkId = netId; + } else { + if (disableOthers) { + mTargetNetworkId = netId; + } + mWifiConnectivityManager.forceConnectivityScan(); } replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); @@ -5770,24 +5855,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss && targetWificonfiguration.networkId == networkId && targetWificonfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.IEEE8021X) - && (eapMethod == WifiEnterpriseConfig.Eap.SIM - || eapMethod == WifiEnterpriseConfig.Eap.AKA - || eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME)) { - TelephonyManager tm = (TelephonyManager) - mContext.getSystemService(Context.TELEPHONY_SERVICE); - if (tm != null) { - String imsi = tm.getSubscriberId(); - String mccMnc = ""; - - if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) - mccMnc = tm.getSimOperator(); - - String identity = buildIdentity(eapMethod, imsi, mccMnc); - - if (!identity.isEmpty()) { - mWifiNative.simIdentityResponse(networkId, identity); - identitySent = true; - } + && TelephonyUtil.isSimEapMethod(eapMethod)) { + String identity = TelephonyUtil.getSimIdentity(mContext, eapMethod); + if (identity != null) { + mWifiNative.simIdentityResponse(networkId, identity); + identitySent = true; } } if (!identitySent) { @@ -5829,12 +5901,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss replyToMessage(message, message.what, mWifiConfigManager.getMatchingConfig((ScanResult)message.obj)); break; - /* Do a redundant disconnect without transition */ - case CMD_DISCONNECT: - mWifiConfigManager.setAndEnableLastSelectedConfiguration - (WifiConfiguration.INVALID_NETWORK_ID); - mWifiNative.disconnect(); - break; case CMD_RECONNECT: if (mWifiConnectivityManager != null) { mWifiConnectivityManager.forceConnectivityScan(); @@ -6810,7 +6876,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiConfigManager, mNetworkAgent, mWifiScoreReport, - mAggressiveHandover); + mAggressiveHandover, + mWifiMetrics); } sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS); @@ -6882,10 +6949,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss stopRssiMonitoringOffload(); break; case CMD_RESET_SIM_NETWORKS: - if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { + if (message.arg1 == 0 // sim was removed + && mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(mLastNetworkId); - if (mWifiConfigManager.isSimConfig(config)) { + if (TelephonyUtil.isSimConfig(config)) { mWifiNative.disconnect(); transitionTo(mDisconnectingState); } @@ -7276,8 +7344,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss break; case CMD_UNWANTED_NETWORK: if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) { - mWifiConfigManager.handleBadNetworkDisconnectReport( - mLastNetworkId, mWifiInfo); mWifiNative.disconnect(); transitionTo(mDisconnectingState); } else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN || @@ -7422,18 +7488,16 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss config = mWifiConfigManager.getWifiConfiguration(netId); } + setTargetBssid(config, bssid); + mTargetNetworkId = netId; + logd("CMD_AUTO_ROAM sup state " + mSupplicantStateTracker.getSupplicantStateName() + " my state " + getCurrentState().getName() + " nid=" + Integer.toString(netId) + " config " + config.configKey() - + " roam=" + Integer.toString(message.arg2) - + " to " + bssid + " targetRoamBSSID " + mTargetRoamBSSID); - setTargetBssid(config, bssid); - mTargetNetworkId = netId; - /* Determine if this is a regular roam (between BSSIDs sharing the same SSID), or a DBDC roam (between 2.4 & 5GHz networks on different SSID's, but with matching 16 byte BSSID prefixes): @@ -7583,6 +7647,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_START_SCAN: deferMessage(message); return HANDLED; + case CMD_DISCONNECT: + if (DBG) log("Ignore CMD_DISCONNECT when already disconnecting."); + break; case CMD_DISCONNECTING_WATCHDOG_TIMER: if (disconnectingWatchdogCount == message.arg1) { if (DBG) log("disconnecting watchdog! -> disconnect"); @@ -7684,7 +7751,19 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss setAndEnableLastSelectedConfiguration( WifiConfiguration.INVALID_NETWORK_ID); break; - /* Ignore network disconnect */ + case CMD_DISCONNECT: + if (SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) { + if (DBG) { + log("CMD_DISCONNECT when supplicant is connecting - do not ignore"); + } + mWifiConfigManager.setAndEnableLastSelectedConfiguration( + WifiConfiguration.INVALID_NETWORK_ID); + mWifiNative.disconnect(); + break; + } + if (DBG) log("Ignore CMD_DISCONNECT when already disconnected."); + break; + /* Ignore network disconnect */ case WifiMonitor.NETWORK_DISCONNECTION_EVENT: // Interpret this as an L2 connection failure break; @@ -7877,11 +7956,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss sendMessage(CMD_AP_STOPPED); } else if (state == WIFI_AP_STATE_FAILED) { sendMessage(CMD_START_AP_FAILURE); - } else if (state == WifiManager.WIFI_AP_STATE_RESTART) { - sendMessage(CMD_AP_STOPPED); - sendMessage(CMD_START_AP, null); - return; } + setWifiApState(state, reason); } } @@ -7906,7 +7982,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss checkAndSetConnectivityInstance(); mSoftApManager = mFacade.makeSoftApManager( mContext, getHandler().getLooper(), mWifiNative, mNwService, - mCm, mCountryCode.getCurrentCountryCode(), + mCm, mCountryCode.getCountryCode(), mWifiApConfigStore.getAllowed2GChannel(), new SoftApListener()); mSoftApManager.start(config); @@ -8117,6 +8193,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return result; } + // TODO move to TelephonyUtil, same with utilities above String getGsmSimAuthResponse(String[] requestData, TelephonyManager tm) { StringBuilder sb = new StringBuilder(); for (String challenge : requestData) { @@ -8178,6 +8255,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss return sb.toString(); } + // TODO move to TelephonyUtil void handleGsmAuthRequest(SimAuthRequestData requestData) { if (targetWificonfiguration == null || targetWificonfiguration.networkId == requestData.networkId) { @@ -8205,6 +8283,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } + // TODO move to TelephonyUtil void handle3GAuthRequest(SimAuthRequestData requestData) { StringBuilder sb = new StringBuilder(); byte[] rand = null; @@ -8293,7 +8372,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss * @param bssid BSSID of the network */ public void autoConnectToNetwork(int networkId, String bssid) { - sendMessage(CMD_AUTO_CONNECT, networkId, 0, bssid); + synchronized (mWifiReqCountLock) { + if (hasConnectionRequests()) { + sendMessage(CMD_AUTO_CONNECT, networkId, 0, bssid); + } + } } /** @@ -8344,6 +8427,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss int numEnterpriseNetworks = 0; int numNetworksAddedByUser = 0; int numNetworksAddedByApps = 0; + int numHiddenNetworks = 0; + int numPasspoint = 0; for (WifiConfiguration config : mWifiConfigManager.getSavedNetworks()) { if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) { numOpenNetworks++; @@ -8357,6 +8442,12 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } else { numNetworksAddedByApps++; } + if (config.hiddenSSID) { + numHiddenNetworks++; + } + if (config.isPasspoint()) { + numPasspoint++; + } } mWifiMetrics.setNumSavedNetworks(numSavedNetworks); mWifiMetrics.setNumOpenNetworks(numOpenNetworks); @@ -8364,17 +8455,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mWifiMetrics.setNumEnterpriseNetworks(numEnterpriseNetworks); mWifiMetrics.setNumNetworksAddedByUser(numNetworksAddedByUser); mWifiMetrics.setNumNetworksAddedByApps(numNetworksAddedByApps); - - /* <TODO> decide how to access WifiServiecImpl.isLocationEnabled() or if to do it manually - mWifiMetrics.setIsLocationEnabled(Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) - != Settings.Secure.LOCATION_MODE_OFF); - */ - - /* <TODO> decide how statemachine will access WifiSettingsStore - mWifiMetrics.setIsScanningAlwaysEnabled(mSettingsStore.isScanningAlwaysAvailable()); - */ + mWifiMetrics.setNumHiddenNetworks(numHiddenNetworks); + mWifiMetrics.setNumPasspointNetworks(numPasspoint); } private static String getLinkPropertiesSummary(LinkProperties lp) { @@ -8473,4 +8555,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } } + /** + * Check if there is any connection request for WiFi network. + * Note, caller of this helper function must acquire mWifiReqCountLock. + */ + private boolean hasConnectionRequests() { + return mConnectionReqCount > 0 || mUntrustedReqCount > 0; + } } diff --git a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java index 070c69b95..9bcfddbb6 100644 --- a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java +++ b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java @@ -173,26 +173,25 @@ public class ConfigBuilder { List<X509Certificate> clientChain, PrivateKey key) throws IOException, GeneralSecurityException { - Credential credential = homeSP.getCredential(); - WifiConfiguration config; - EAP.EAPMethodID eapMethodID = credential.getEAPMethod().getEAPMethodID(); + EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID(); switch (eapMethodID) { case EAP_TTLS: if (key != null || clientChain != null) { - Log.w(TAG, "Client cert and/or key included with EAP-TTLS profile"); + Log.w(TAG, "Client cert and/or key unnecessarily included with EAP-TTLS "+ + "profile"); } - config = buildTTLSConfig(homeSP); + config = buildTTLSConfig(homeSP, caCert); break; case EAP_TLS: - config = buildTLSConfig(homeSP, clientChain, key); + config = buildTLSConfig(homeSP, clientChain, key, caCert); break; case EAP_AKA: case EAP_AKAPrim: case EAP_SIM: if (key != null || clientChain != null || caCert != null) { - Log.i(TAG, "Client/CA cert and/or key included with " + + Log.i(TAG, "Client/CA cert and/or key unnecessarily included with " + eapMethodID + " profile"); } config = buildSIMConfig(homeSP); @@ -201,11 +200,6 @@ public class ConfigBuilder { throw new IOException("Unsupported EAP Method: " + eapMethodID); } - WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; - - enterpriseConfig.setCaCertificate(caCert); - enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm()); - return config; } @@ -233,7 +227,28 @@ public class ConfigBuilder { } */ - private static WifiConfiguration buildTTLSConfig(HomeSP homeSP) + private static void setAnonymousIdentityToNaiRealm( + WifiConfiguration config, Credential credential) { + /** + * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so + * that this value will be sent to the EAP server as part of the EAP-Response/ Identity + * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity + * packet, and revert to using the (real) identity field for subsequent transactions that + * request an identity (e.g. in EAP-TTLS). + * + * This NAI realm value (the portion of the identity after the '@') is used to tell the + * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a + * placeholder that is not used--it is set to this value by convention. See Section 5.1 of + * RFC3748 for more details. + * + * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the + * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to + * identify the device. + */ + config.enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm()); + } + + private static WifiConfiguration buildTTLSConfig(HomeSP homeSP, X509Certificate caCert) throws IOException { Credential credential = homeSP.getCredential(); @@ -255,13 +270,17 @@ public class ConfigBuilder { enterpriseConfig.setPhase2Method(remapInnerMethod(ttlsParam.getType())); enterpriseConfig.setIdentity(credential.getUserName()); enterpriseConfig.setPassword(credential.getPassword()); + enterpriseConfig.setCaCertificate(caCert); + + setAnonymousIdentityToNaiRealm(config, credential); return config; } private static WifiConfiguration buildTLSConfig(HomeSP homeSP, List<X509Certificate> clientChain, - PrivateKey clientKey) + PrivateKey clientKey, + X509Certificate caCert) throws IOException, GeneralSecurityException { Credential credential = homeSP.getCredential(); @@ -296,6 +315,9 @@ public class ConfigBuilder { WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; enterpriseConfig.setClientCertificateAlias(alias); enterpriseConfig.setClientKeyEntry(clientKey, clientCertificate); + enterpriseConfig.setCaCertificate(caCert); + + setAnonymousIdentityToNaiRealm(config, credential); return config; } diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java index 402c0a869..321254c72 100644 --- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java +++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java @@ -59,6 +59,8 @@ public class NetworkDetail { private final String mSSID; private final long mHESSID; private final long mBSSID; + // True if the SSID is potentially from a hidden network + private final boolean mIsHiddenSsid; // BSS Load element: private final int mStationCount; @@ -131,6 +133,7 @@ public class NetworkDetail { mBSSID = Utils.parseMac(bssid); String ssid = null; + boolean isHiddenSsid = false; byte[] ssidOctets = null; InformationElementUtil.BssLoad bssLoad = new InformationElementUtil.BssLoad(); @@ -236,10 +239,18 @@ public class NetworkDetail { ssid = new String(ssidOctets, StandardCharsets.ISO_8859_1); } } + isHiddenSsid = true; + for (byte byteVal : ssidOctets) { + if (byteVal != 0) { + isHiddenSsid = false; + break; + } + } } mSSID = ssid; mHESSID = interworking.hessid; + mIsHiddenSsid = isHiddenSsid; mStationCount = bssLoad.stationCount; mChannelUtilization = bssLoad.channelUtilization; mCapacity = bssLoad.capacity; @@ -268,7 +279,9 @@ public class NetworkDetail { } // If trafficIndicationMap is not valid, mDtimPeriod will be negative - mDtimInterval = trafficIndicationMap.mDtimPeriod; + if (trafficIndicationMap.isValid()) { + mDtimInterval = trafficIndicationMap.mDtimPeriod; + } int maxRateA = 0; int maxRateB = 0; @@ -318,6 +331,7 @@ public class NetworkDetail { private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) { mSSID = base.mSSID; + mIsHiddenSsid = base.mIsHiddenSsid; mBSSID = base.mBSSID; mHESSID = base.mHESSID; mStationCount = base.mStationCount; @@ -515,6 +529,28 @@ public class NetworkDetail { return toMACString(mBSSID); } + /** + * Evaluates the ScanResult this NetworkDetail is built from + * returns true if built from a Beacon Frame + * returns false if built from a Probe Response + */ + public boolean isBeaconFrame() { + // Beacon frames have a 'Traffic Indication Map' Information element + // Probe Responses do not. This is indicated by a DTIM period > 0 + return mDtimInterval > 0; + } + + /** + * Evaluates the ScanResult this NetworkDetail is built from + * returns true if built from a hidden Beacon Frame + * returns false if not hidden or not a Beacon + */ + public boolean isHiddenBeaconFrame() { + // Hidden networks are not 80211 standard, but it is common for a hidden network beacon + // frame to either send zero-value bytes as the SSID, or to send no bytes at all. + return isBeaconFrame() && mIsHiddenSsid; + } + public static String toMACString(long mac) { StringBuilder sb = new StringBuilder(); boolean first = true; @@ -528,5 +564,4 @@ public class NetworkDetail { } return sb.toString(); } - } diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java index 5b82167e5..ad259d653 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java @@ -1376,8 +1376,20 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { WifiP2pDevice owner = group.getOwner(); if (owner == null) { - loge("Ignored invitation from null owner"); - break; + int id = group.getNetworkId(); + if (id < 0) { + loge("Ignored invitation from null owner"); + break; + } + + String addr = mGroups.getOwnerAddr(id); + if (addr != null) { + group.setOwner(new WifiP2pDevice(addr)); + owner = group.getOwner(); + } else { + loge("Ignored invitation from null owner"); + break; + } } config = new WifiP2pConfig(); diff --git a/service/java/com/android/server/wifi/scanner/ChannelHelper.java b/service/java/com/android/server/wifi/scanner/ChannelHelper.java index d4168123d..acb0ac820 100644 --- a/service/java/com/android/server/wifi/scanner/ChannelHelper.java +++ b/service/java/com/android/server/wifi/scanner/ChannelHelper.java @@ -100,6 +100,10 @@ public abstract class ChannelHelper { */ public abstract boolean isEmpty(); /** + * @return true if the collection contains all available channels + */ + public abstract boolean isAllChannels(); + /** * Remove all channels from the collection */ public abstract void clear(); diff --git a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java index b180da71d..acddc26c6 100644 --- a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java +++ b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java @@ -198,6 +198,12 @@ public class KnownBandsChannelHelper extends ChannelHelper { } @Override + public boolean isAllChannels() { + return getAvailableScanChannels(WifiScanner.WIFI_BAND_BOTH_WITH_DFS).length == + mChannels.size(); + } + + @Override public void clear() { mAllBands = 0; mExactBands = 0; diff --git a/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java b/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java index 7b1602008..4f8373b30 100644 --- a/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java +++ b/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java @@ -112,6 +112,11 @@ public class NoBandChannelHelper extends ChannelHelper { } @Override + public boolean isAllChannels() { + return mAllChannels; + } + + @Override public void clear() { mAllChannels = false; mChannels.clear(); diff --git a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java index ab013b1e0..ac5db5a4f 100644 --- a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java @@ -330,6 +330,11 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle processPendingScans(); } + private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) { + return (mLastScanSettings == null || !Arrays.equals( + newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList)); + } + private void processPendingScans() { synchronized (mSettingsLock) { // Wait for the active scan result to come back to reschedule other scans, @@ -460,8 +465,16 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle // TODO(b/27769665) background scans should be failed too if scans fail enough } } else if (isHwPnoScanRequired()) { - newScanSettings.setHwPnoScan(mPnoEventHandler); - if (startHwPnoScan()) { + newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler); + boolean status; + // If the PNO network list has changed from the previous request, ensure that + // we bypass the debounce logic and restart PNO scan. + if (isDifferentPnoScanSettings(newScanSettings)) { + status = restartHwPnoScan(); + } else { + status = startHwPnoScan(); + } + if (status) { mLastScanSettings = newScanSettings; } else { Log.e(TAG, "Failed to start PNO scan"); @@ -625,7 +638,8 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle } } Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); - mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, + mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0, + mLastScanSettings.singleScanFreqs.isAllChannels(), singleScanResults.toArray(new ScanResult[singleScanResults.size()])); mLastScanSettings.singleScanEventHandler .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); @@ -695,6 +709,11 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle mHwPnoDebouncer.forceStopPnoScan(); } + private boolean restartHwPnoScan() { + mHwPnoDebouncer.forceStopPnoScan(); + return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener); + } + /** * Hw Pno Scan is required only for disconnected PNO when the device supports it. * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. @@ -826,10 +845,14 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle } public boolean hwPnoScanActive = false; + public WifiNative.PnoNetwork[] pnoNetworkList; public WifiNative.PnoEventHandler pnoScanEventHandler; - public void setHwPnoScan(WifiNative.PnoEventHandler pnoScanEventHandler) { + public void setHwPnoScan( + WifiNative.PnoNetwork[] pnoNetworkList, + WifiNative.PnoEventHandler pnoScanEventHandler) { hwPnoScanActive = true; + this.pnoNetworkList = pnoNetworkList; this.pnoScanEventHandler = pnoScanEventHandler; } } @@ -1050,7 +1073,6 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle if (DBG) Log.d(TAG, "PNO state is already " + enable); return true; } - mLastPnoChangeTimeStamp = mClock.elapsedRealtime(); if (mWifiNative.setPnoScan(enable)) { Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable); @@ -1058,6 +1080,7 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle return true; } else { Log.e(TAG, "PNO state change to " + enable + " failed"); + mCurrentPnoState = false; return false; } } @@ -1125,15 +1148,13 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle * scan immediately. */ public void forceStopPnoScan() { - if (mCurrentPnoState) { - if (DBG) Log.d(TAG, "Force stopping Pno scan"); - // Cancel the debounce timer and stop PNO scan. - if (mWaitForTimer) { - mAlarmManager.cancel(mAlarmListener); - mWaitForTimer = false; - } - updatePnoState(false); + if (DBG) Log.d(TAG, "Force stopping Pno scan"); + // Cancel the debounce timer and stop PNO scan. + if (mWaitForTimer) { + mAlarmManager.cancel(mAlarmListener); + mWaitForTimer = false; } + updatePnoState(false); } } } diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 7108cb857..9f8fb2f58 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -76,7 +76,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms private static final int UNKNOWN_PID = -1; - private final LocalLog mLocalLog = new LocalLog(1024); + private final LocalLog mLocalLog = new LocalLog(512); private void localLog(String message) { mLocalLog.log(message); @@ -136,7 +136,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo); if (client != null) { - logw("duplicate client connection: " + msg.sendingUid); + logw("duplicate client connection: " + msg.sendingUid + ", messenger=" + + msg.replyTo); client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED); return; @@ -151,7 +152,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL); - if (DBG) Log.d(TAG, "client connected: " + client); + localLog("client connected: " + client); return; } case AsyncChannel.CMD_CHANNEL_DISCONNECT: { @@ -163,10 +164,10 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo); - if (client != null) { - if (DBG) { - Log.d(TAG, "client disconnected: " + client + ", reason: " + msg.arg1); - } + if (client != null && msg.arg1 != AsyncChannel.STATUS_SEND_UNSUCCESSFUL + && msg.arg1 + != AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED) { + localLog("client disconnected: " + client + ", reason: " + msg.arg1); client.cleanup(); } return; @@ -215,6 +216,15 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { case WifiScanner.CMD_STOP_TRACKING_CHANGE: mWifiChangeStateMachine.sendMessage(Message.obtain(msg)); break; + case WifiScanner.CMD_REGISTER_SCAN_LISTENER: + logScanRequest("registerScanListener", ci, msg.arg2, null, null, null); + mSingleScanListeners.addRequest(ci, msg.arg2, null, null); + replySucceeded(msg); + break; + case WifiScanner.CMD_DEREGISTER_SCAN_LISTENER: + logScanRequest("deregisterScanListener", ci, msg.arg2, null, null, null); + mSingleScanListeners.removeRequest(ci, msg.arg2); + break; default: replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request"); break; @@ -243,6 +253,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final WifiScannerImpl.WifiScannerImplFactory mScannerImplFactory; private final ArrayMap<Messenger, ClientInfo> mClients; + private final RequestList<Void> mSingleScanListeners = new RequestList<>(); + private ChannelHelper mChannelHelper; private BackgroundScanScheduler mBackgroundScheduler; private WifiNative.ScanSettings mPreviousSchedule; @@ -419,6 +431,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private final IdleState mIdleState = new IdleState(); private final ScanningState mScanningState = new ScanningState(); + private WifiNative.ScanSettings mActiveScanSettings = null; private RequestList<ScanSettings> mActiveScans = new RequestList<>(); private RequestList<ScanSettings> mPendingScans = new RequestList<>(); @@ -547,12 +560,24 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY); WorkSource workSource = scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY); - if (validateAndAddToScanQueue(ci, handler, scanSettings, workSource)) { + if (validateScanRequest(ci, handler, scanSettings, workSource)) { + logScanRequest("addSingleScanRequest", ci, handler, workSource, + scanSettings, null); replySucceeded(msg); + + // If there is an active scan that will fulfill the scan request then + // mark this request as an active scan, otherwise mark it pending. // If were not currently scanning then try to start a scan. Otherwise // this scan will be scheduled when transitioning back to IdleState // after finishing the current scan. - if (getCurrentState() != mScanningState) { + if (getCurrentState() == mScanningState) { + if (activeScanSatisfies(scanSettings)) { + mActiveScans.addRequest(ci, handler, workSource, scanSettings); + } else { + mPendingScans.addRequest(ci, handler, workSource, scanSettings); + } + } else { + mPendingScans.addRequest(ci, handler, workSource, scanSettings); tryToStartNewScan(); } } else { @@ -598,6 +623,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public void exit() { + mActiveScanSettings = null; try { mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource); } catch (RemoteException e) { @@ -639,7 +665,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - boolean validateAndAddToScanQueue(ClientInfo ci, int handler, ScanSettings settings, + boolean validateScanRequest(ClientInfo ci, int handler, ScanSettings settings, WorkSource workSource) { if (ci == null) { Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); @@ -651,8 +677,46 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { return false; } } - logScanRequest("addSingleScanRequest", ci, handler, workSource, settings, null); - mPendingScans.addRequest(ci, handler, workSource, settings); + return true; + } + + boolean activeScanSatisfies(ScanSettings settings) { + if (mActiveScanSettings == null) { + return false; + } + + // there is always one bucket for a single scan + WifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0]; + + // validate that all requested channels are being scanned + ChannelCollection activeChannels = mChannelHelper.createChannelCollection(); + activeChannels.addChannels(activeBucket); + if (!activeChannels.containsSettings(settings)) { + return false; + } + + // if the request is for a full scan, but there is no ongoing full scan + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 + && (activeBucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) + == 0) { + return false; + } + + if (settings.hiddenNetworkIds != null) { + if (mActiveScanSettings.hiddenNetworkIds == null) { + return false; + } + Set<Integer> activeHiddenNetworkIds = new HashSet<>(); + for (int id : mActiveScanSettings.hiddenNetworkIds) { + activeHiddenNetworkIds.add(id); + } + for (int id : settings.hiddenNetworkIds) { + if (!activeHiddenNetworkIds.contains(id)) { + return false; + } + } + } + return true; } @@ -711,6 +775,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; if (mScannerImpl.startSingleScan(settings, this)) { + // store the active scan settings + mActiveScanSettings = settings; // swap pending and active scan requests RequestList<ScanSettings> tmp = mActiveScans; mActiveScans = mPendingScans; @@ -745,6 +811,10 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); } } + + for (RequestInfo<Void> entry : mSingleScanListeners) { + entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result); + } } void reportScanResults(ScanData results) { @@ -755,18 +825,26 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mWifiMetrics.incrementEmptyScanResultCount(); } } + ScanData[] allResults = new ScanData[] {results}; for (RequestInfo<ScanSettings> entry : mActiveScans) { - ScanData[] resultsArray = new ScanData[] {results}; ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings( - mChannelHelper, resultsArray, entry.settings, -1); - WifiScanner.ParcelableScanData parcelableScanData = + mChannelHelper, allResults, entry.settings, -1); + WifiScanner.ParcelableScanData parcelableResultsToDeliver = new WifiScanner.ParcelableScanData(resultsToDeliver); logCallback("singleScanResults", entry.clientInfo, entry.handlerId, describeForLog(resultsToDeliver)); - entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableScanData); + entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver); // make sure the handler is removed entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null); } + + WifiScanner.ParcelableScanData parcelableAllResults = + new WifiScanner.ParcelableScanData(allResults); + for (RequestInfo<Void> entry : mSingleScanListeners) { + logCallback("singleScanResults", entry.clientInfo, entry.handlerId, + describeForLog(allResults)); + entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults); + } } } @@ -976,8 +1054,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { replySucceeded(msg); break; case WifiScanner.CMD_SET_HOTLIST: - addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj); - replySucceeded(msg); + if (addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj)) { + replySucceeded(msg); + } else { + replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); + } break; case WifiScanner.CMD_RESET_HOTLIST: removeHotlist(ci, msg.arg2); @@ -1117,13 +1198,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { if (DBG) Log.d(TAG, "scan stopped"); return true; } else { - Log.d(TAG, "starting scan: " + localLog("starting scan: " + "base period=" + schedule.base_period_ms + ", max ap per scan=" + schedule.max_ap_per_scan + ", batched scans=" + schedule.report_threshold_num_scans); for (int b = 0; b < schedule.num_buckets; b++) { WifiNative.BucketSettings bucket = schedule.buckets[b]; - Log.d(TAG, "bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" + localLog("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)" + "[" + bucket.report_events + "]: " + ChannelHelper.toString(bucket)); } @@ -1214,14 +1295,22 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mActiveBackgroundScans.clear(); } - private void addHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) { + private boolean addHotlist(ClientInfo ci, int handler, + WifiScanner.HotlistSettings settings) { + if (ci == null) { + Log.d(TAG, "Failing hotlist request ClientInfo not found " + handler); + return false; + } mActiveHotlistSettings.addRequest(ci, handler, null, settings); resetHotlist(); + return true; } private void removeHotlist(ClientInfo ci, int handler) { - mActiveHotlistSettings.removeRequest(ci, handler); - resetHotlist(); + if (ci != null) { + mActiveHotlistSettings.removeRequest(ci, handler); + resetHotlist(); + } } private void resetHotlist() { @@ -1820,6 +1909,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } public void cleanup() { + mSingleScanListeners.removeAllForClient(this); mSingleScanStateMachine.removeSingleScanRequests(this); mBackgroundScanStateMachine.removeBackgroundScanSettings(this); mBackgroundScanStateMachine.removeHotlistSettings(this); @@ -1891,7 +1981,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public String toString() { - return "ClientInfo[uid=" + mUid + "]"; + return "ClientInfo[uid=" + mUid + "," + mMessenger + "]"; } } @@ -1988,6 +2078,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { public void sendRequestToClientHandler(int what) { sendRequestToClientHandler(what, null, null); } + + @Override + public String toString() { + return "InternalClientInfo[]"; + } } void replySucceeded(Message msg) { @@ -2080,9 +2175,12 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { case WifiScanner.CMD_START_TRACKING_CHANGE: - addWifiChangeHandler(ci, msg.arg2); - replySucceeded(msg); - transitionTo(mMovingState); + if (addWifiChangeHandler(ci, msg.arg2)) { + replySucceeded(msg); + transitionTo(mMovingState); + } else { + replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); + } break; case WifiScanner.CMD_STOP_TRACKING_CHANGE: // nothing to do @@ -2114,8 +2212,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { case WifiScanner.CMD_START_TRACKING_CHANGE: - addWifiChangeHandler(ci, msg.arg2); - replySucceeded(msg); + if (addWifiChangeHandler(ci, msg.arg2)) { + replySucceeded(msg); + } else { + replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); + } break; case WifiScanner.CMD_STOP_TRACKING_CHANGE: removeWifiChangeHandler(ci, msg.arg2); @@ -2167,8 +2268,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(msg.replyTo); switch (msg.what) { case WifiScanner.CMD_START_TRACKING_CHANGE: - addWifiChangeHandler(ci, msg.arg2); - replySucceeded(msg); + if (addWifiChangeHandler(ci, msg.arg2)) { + replySucceeded(msg); + } else { + replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); + } break; case WifiScanner.CMD_STOP_TRACKING_CHANGE: removeWifiChangeHandler(ci, msg.arg2); @@ -2394,7 +2498,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - private void addWifiChangeHandler(ClientInfo ci, int handler) { + private boolean addWifiChangeHandler(ClientInfo ci, int handler) { + if (ci == null) { + Log.d(TAG, "Failing wifi change request ClientInfo not found " + handler); + return false; + } mActiveWifiChangeHandlers.add(Pair.create(ci, handler)); // Add an internal client to make background scan requests. if (mInternalClientInfo == null) { @@ -2402,11 +2510,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler())); mInternalClientInfo.register(); } + return true; } private void removeWifiChangeHandler(ClientInfo ci, int handler) { - mActiveWifiChangeHandlers.remove(Pair.create(ci, handler)); - untrackSignificantWifiChangeOnEmpty(); + if (ci != null) { + mActiveWifiChangeHandlers.remove(Pair.create(ci, handler)); + untrackSignificantWifiChangeOnEmpty(); + } } private void untrackSignificantWifiChangeOnEmpty() { @@ -2520,7 +2631,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { StringBuilder sb = new StringBuilder(); sb.append(request) .append(": ") - .append(ci.toString()) + .append((ci == null) ? "ClientInfo[unknown]" : ci.toString()) .append(",Id=") .append(id); if (workSource != null) { @@ -2540,11 +2651,9 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { void logCallback(String callback, ClientInfo ci, int id, String extra) { StringBuilder sb = new StringBuilder(); sb.append(callback) - .append(": "); - if (ci != null) { - sb.append(ci.toString()); - } - sb .append(",Id=") + .append(": ") + .append((ci == null) ? "ClientInfo[unknown]" : ci.toString()) + .append(",Id=") .append(id); if (extra != null) { sb.append(",").append(extra); diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index 6e6dfcc77..d3e2fea90 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -600,7 +600,7 @@ public class InformationElementUtil { } catch (BufferUnderflowException e) { return; } - if (mLength <= MAX_TIM_LENGTH) { + if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) { mValid = true; } } diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java new file mode 100644 index 000000000..3d7ce455c --- /dev/null +++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 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.util; + +import android.content.Context; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; +import android.telephony.TelephonyManager; + +/** + * Utilities for the Wifi Service to interact with telephony. + */ +public class TelephonyUtil { + + /** + * Get the identity for the current SIM or null if the sim is not available + */ + public static String getSimIdentity(Context context, int eapMethod) { + TelephonyManager tm = TelephonyManager.from(context); + if (tm != null) { + String imsi = tm.getSubscriberId(); + String mccMnc = ""; + + if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) { + mccMnc = tm.getSimOperator(); + } + + return buildIdentity(eapMethod, imsi, mccMnc); + } else { + return null; + } + } + + /** + * create Permanent Identity base on IMSI, + * + * rfc4186 & rfc4187: + * identity = usernam@realm + * with username = prefix | IMSI + * and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003) + */ + private static String buildIdentity(int eapMethod, String imsi, String mccMnc) { + if (imsi == null || imsi.isEmpty()) { + return null; + } + + String prefix; + 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 valide EapMethod + return null; + } + + /* extract mcc & mnc from mccMnc */ + String mcc; + String mnc; + 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"; + } + + /** + * Checks if the network is a sim config. + * + * @param config Config corresponding to the network. + * @return true if it is a sim config, false otherwise. + */ + public static boolean isSimConfig(WifiConfiguration config) { + if (config == null || config.enterpriseConfig == null) { + return false; + } + + return isSimEapMethod(config.enterpriseConfig.getEapMethod()); + } + + /** + * Checks if the network is a sim config. + * + * @param method + * @return true if it is a sim config, false otherwise. + */ + public static boolean isSimEapMethod(int eapMethod) { + return eapMethod == WifiEnterpriseConfig.Eap.SIM + || eapMethod == WifiEnterpriseConfig.Eap.AKA + || eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME; + } +} diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp index 80a19c00e..baed910b4 100644 --- a/service/jni/com_android_server_wifi_WifiNative.cpp +++ b/service/jni/com_android_server_wifi_WifiNative.cpp @@ -849,6 +849,13 @@ static jboolean android_net_wifi_setHotlist( return false; } + if (params.num_bssid > + static_cast<int>(sizeof(params.ap) / sizeof(params.ap[0]))) { + ALOGE("setHotlist array length is too long"); + android_errorWriteLog(0x534e4554, "31856351"); + return false; + } + for (int i = 0; i < params.num_bssid; i++) { JNIObject<jobject> objAp = helper.getObjectArrayElement(array, i); @@ -1175,7 +1182,7 @@ static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_resu JNIHelper helper(mVM); - ALOGD("onRttResults called, vm = %p, obj = %p", mVM, mCls); + if (DBG) ALOGD("onRttResults called, vm = %p, obj = %p", mVM, mCls); JNIObject<jobjectArray> rttResults = helper.newObjectArray( num_results, "android/net/wifi/RttManager$RttResult", NULL); @@ -1222,14 +1229,12 @@ static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_resu JNIObject<jobject> LCI = helper.createObject( "android/net/wifi/RttManager$WifiInformationElement"); if (result->LCI != NULL && result->LCI->len > 0) { - ALOGD("Add LCI in result"); helper.setByteField(LCI, "id", result->LCI->id); JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len); jbyte *bytes = (jbyte *)&(result->LCI->data[0]); helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes); helper.setObjectField(LCI, "data", "[B", elements); } else { - ALOGD("No LCI in result"); helper.setByteField(LCI, "id", (byte)(0xff)); } helper.setObjectField(rttResult, "LCI", @@ -1238,14 +1243,12 @@ static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_resu JNIObject<jobject> LCR = helper.createObject( "android/net/wifi/RttManager$WifiInformationElement"); if (result->LCR != NULL && result->LCR->len > 0) { - ALOGD("Add LCR in result"); helper.setByteField(LCR, "id", result->LCR->id); JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len); jbyte *bytes = (jbyte *)&(result->LCR->data[0]); helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes); helper.setObjectField(LCR, "data", "[B", elements); } else { - ALOGD("No LCR in result"); helper.setByteField(LCR, "id", (byte)(0xff)); } helper.setObjectField(rttResult, "LCR", @@ -1266,7 +1269,7 @@ static jboolean android_net_wifi_requestRange( JNIHelper helper(env); wifi_interface_handle handle = getIfaceHandle(helper, cls, iface); - ALOGD("sending rtt request [%d] = %p", id, handle); + if (DBG) ALOGD("sending rtt request [%d] = %p", id, handle); if (params == NULL) { ALOGE("ranging params are empty"); return false; @@ -1284,7 +1287,7 @@ static jboolean android_net_wifi_requestRange( JNIObject<jobject> param = helper.getObjectArrayElement((jobjectArray)params, i); if (param == NULL) { - ALOGD("could not get element %d", i); + ALOGW("could not get element %d", i); continue; } @@ -1309,18 +1312,6 @@ static jboolean android_net_wifi_requestRange( config.burst_duration = (unsigned) helper.getIntField(param, "burstTimeout"); config.preamble = (wifi_rtt_preamble) helper.getIntField(param, "preamble"); config.bw = (wifi_rtt_bw) helper.getIntField(param, "bandwidth"); - - ALOGD("RTT request destination %d: type is %d, peer is %d, bw is %d, center_freq is %d ", i, - config.type,config.peer, config.channel.width, config.channel.center_freq); - ALOGD("center_freq0 is %d, center_freq1 is %d, num_burst is %d,interval is %d", - config.channel.center_freq0, config.channel.center_freq1, config.num_burst, - config.burst_period); - ALOGD("frames_per_burst is %d, retries of measurement frame is %d, retries_per_ftmr is %d", - config.num_frames_per_burst, config.num_retries_per_rtt_frame, - config.num_retries_per_ftmr); - ALOGD("LCI_requestis %d, LCR_request is %d, burst_timeout is %d, preamble is %d, bw is %d", - config.LCI_request, config.LCR_request, config.burst_duration, config.preamble, - config.bw); } wifi_rtt_event_handler handler; @@ -1334,7 +1325,7 @@ static jboolean android_net_wifi_cancelRange( JNIHelper helper(env); wifi_interface_handle handle = getIfaceHandle(helper, cls, iface); - ALOGD("cancelling rtt request [%d] = %p", id, handle); + if (DBG) ALOGD("cancelling rtt request [%d] = %p", id, handle); if (params == NULL) { ALOGE("ranging params are empty"); @@ -1353,7 +1344,7 @@ static jboolean android_net_wifi_cancelRange( JNIObject<jobject> param = helper.getObjectArrayElement(params, i); if (param == NULL) { - ALOGD("could not get element %d", i); + ALOGW("could not get element %d", i); continue; } diff --git a/service/proto/wifi.proto b/service/proto/wifi.proto index 43a4166dc..8128ec115 100644 --- a/service/proto/wifi.proto +++ b/service/proto/wifi.proto @@ -186,6 +186,49 @@ message WifiLog { // The time duration represented by this wifi log, from start to end of capture optional int32 record_duration_sec = 34; + + // Counts the occurrences of each individual RSSI poll level + repeated RssiPollCount rssi_poll_rssi_count = 35; + + // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger, + // without new networks becoming available. + optional int32 num_last_resort_watchdog_successes = 36; + + // Counts the occurrences of each alert reason. + repeated AlertReasonCount alert_reason_count = 47; + + // Total number of saved hidden networks + optional int32 num_hidden_networks = 37; + + // Total number of saved passpoint / hotspot 2.0 networks + optional int32 num_passpoint_networks = 38; + + // Total number of scan results + optional int32 num_total_scan_results = 39; + + // Total number of scan results for open networks + optional int32 num_open_network_scan_results = 40; + + // Total number of scan results for personal networks + optional int32 num_personal_network_scan_results = 41; + + // Total number of scan results for enterprise networks + optional int32 num_enterprise_network_scan_results = 42; + + // Total number of scan results for hidden networks + optional int32 num_hidden_network_scan_results = 43; + + // Total number of scan results for hotspot 2.0 r1 networks + optional int32 num_hotspot2_r1_network_scan_results = 44; + + // Total number of scan results for hotspot 2.0 r2 networks + optional int32 num_hotspot2_r2_network_scan_results = 45; + + // Total number of scans handled by framework (oneshot or otherwise) + optional int32 num_scans = 46; + + // Counts the occurrences of each Wifi score + repeated WifiScoreCount wifi_score_count = 48; } // Information that gets logged for every WiFi connection. @@ -257,7 +300,7 @@ message RouterFingerPrint { // Authentication scheme of the router. optional Auth authentication = 4; - // If the router is hidded. + // If the router is hidden. optional bool hidden = 5; // Channel information. @@ -265,6 +308,9 @@ message RouterFingerPrint { // whether ipv6 is supported. optional bool supports_ipv6 = 7; + + // If the router is a passpoint / hotspot 2.0 network + optional bool passpoint = 8; } message ConnectionEvent { @@ -337,3 +383,30 @@ message ConnectionEvent { // Has bug report been taken. optional bool automatic_bug_report_taken = 9; } + +// Number of occurrences of a specific RSSI poll rssi value +message RssiPollCount { + // RSSI + optional int32 rssi = 1; + + // Number of RSSI polls with 'rssi' + optional int32 count = 2; +} + +// Number of occurrences of a specific alert reason value +message AlertReasonCount { + // Alert reason + optional int32 reason = 1; + + // Number of alerts with |reason|. + optional int32 count = 2; +} + +// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport +message WifiScoreCount { + // Wifi Score + optional int32 score = 1; + + // Number of Wifi score reports with this score + optional int32 count = 2; +} diff --git a/tests/wifitests/src/com/android/server/wifi/ScanResults.java b/tests/wifitests/src/com/android/server/wifi/ScanResults.java index 160902081..fc532701f 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanResults.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanResults.java @@ -138,21 +138,29 @@ public class ScanResults { * @see #generateNativeResults for more details on how results are generated */ public static ScanResults create(int id, int... freqs) { - return new ScanResults(id, -1, generateNativeResults(id, freqs)); + return create(id, generateNativeResults(id, freqs)); + } + public static ScanResults create(int id, boolean allChannelsScanned, int... freqs) { + return create(id, allChannelsScanned, generateNativeResults(id, freqs)); } /** * Create a ScanResults with no IE information. */ public static ScanResults createWithNoIE(int id, int... freqs) { - return new ScanResults(id, -1, generateNativeResults(false, id, freqs)); + return create(id, generateNativeResults(false, id, freqs)); } /** * Create a ScanResults with the given ScanDetails */ public static ScanResults create(int id, ScanDetail... nativeResults) { - return new ScanResults(id, -1, nativeResults); + return new ScanResults(id, false, -1, nativeResults); + } + + public static ScanResults create(int id, boolean allChannelsScanned, + ScanDetail... nativeResults) { + return new ScanResults(id, allChannelsScanned, -1, nativeResults); } /** @@ -162,10 +170,11 @@ public class ScanResults { */ public static ScanResults createOverflowing(int id, int maxResults, ScanDetail... nativeResults) { - return new ScanResults(id, maxResults, nativeResults); + return new ScanResults(id, false, maxResults, nativeResults); } - private ScanResults(int id, int maxResults, ScanDetail... nativeResults) { + private ScanResults(int id, boolean allChannelsScanned, int maxResults, + ScanDetail... nativeResults) { mScanResults = new ScanResult[nativeResults.length]; for (int i = 0; i < nativeResults.length; ++i) { mScanDetails.add(nativeResults[i]); @@ -173,13 +182,13 @@ public class ScanResults { } ScanResult[] sortedScanResults = Arrays.copyOf(mScanResults, mScanResults.length); Arrays.sort(sortedScanResults, SCAN_RESULT_RSSI_COMPARATOR); - mRawScanData = new ScanData(id, 0, sortedScanResults); + mRawScanData = new ScanData(id, 0, 0, allChannelsScanned, sortedScanResults); if (maxResults == -1) { mScanData = mRawScanData; } else { ScanResult[] reducedScanResults = Arrays.copyOf(sortedScanResults, Math.min(sortedScanResults.length, maxResults)); - mScanData = new ScanData(id, 0, reducedScanResults); + mScanData = new ScanData(id, 0, 0, allChannelsScanned, reducedScanResults); } } diff --git a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java index 8228118d8..7a1bdd20b 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java @@ -217,7 +217,7 @@ public class ScanTestUtil { for (int i = 0; i < freqs.length; ++i) { results[i] = createScanResult(freqs[i]); } - return new ScanData(0, 0, bucketsScanned, results); + return new ScanData(0, 0, bucketsScanned, false, results); } public static ScanData[] createScanDatas(int[][] freqs, int[] bucketsScanned) { @@ -272,6 +272,8 @@ public class ScanTestUtil { assertNotNull(prefix + "actual ScanData was null", actual); assertEquals(prefix + "id", expected.getId(), actual.getId()); assertEquals(prefix + "flags", expected.getFlags(), actual.getFlags()); + assertEquals(prefix + "all channels", expected.isAllChannelsScanned(), + actual.isAllChannelsScanned()); assertScanResultsEquals(prefix, expected.getResults(), actual.getResults()); } diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java index c091517d7..b91df5ae3 100644 --- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java @@ -20,7 +20,6 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,7 +29,6 @@ import android.content.Context; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; -import android.net.LinkAddress; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.INetworkManagementService; @@ -69,12 +67,6 @@ public class SoftApManagerTest { @Mock SoftApManager.Listener mListener; @Mock InterfaceConfiguration mInterfaceConfiguration; - /** - * Internal BroadcastReceiver that SoftApManager uses to listen for tethering - * events from ConnectivityManager. - */ - BroadcastReceiver mBroadcastReceiver; - SoftApManager mSoftApManager; /** Sets up test. */ @@ -89,19 +81,12 @@ public class SoftApManagerTest { when(mConnectivityManager.getTetherableWifiRegexs()) .thenReturn(AVAILABLE_DEVICES); - mSoftApManager = new SoftApManager(mContext, - mLooper.getLooper(), + mSoftApManager = new SoftApManager(mLooper.getLooper(), mWifiNative, mNmService, - mConnectivityManager, TEST_COUNTRY_CODE, mAllowed2GChannels, mListener); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver( - broadcastReceiverCaptor.capture(), any(IntentFilter.class)); - mBroadcastReceiver = broadcastReceiverCaptor.getValue(); mLooper.dispatchAll(); } @@ -119,35 +104,6 @@ public class SoftApManagerTest { WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL); } - /** Tests the handling of timeout after tethering is started. */ - @Test - public void tetheringTimedOut() throws Exception { - startSoftApAndVerifyEnabled(); - announceAvailableForTethering(); - verifyTetheringRequested(); - - InOrder order = inOrder(mListener); - - /* Move the time forward to simulate notification timeout. */ - mLooper.moveTimeForward(5000); - mLooper.dispatchAll(); - - /* Verify soft ap is disabled. */ - verify(mNmService).stopAccessPoint(eq(TEST_INTERFACE_NAME)); - order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); - order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); - } - - /** Tests the handling of tethered notification after tethering is started. */ - @Test - public void tetherCompleted() throws Exception { - startSoftApAndVerifyEnabled(); - announceAvailableForTethering(); - verifyTetheringRequested(); - announceTethered(); - verifySoftApNotDisabled(); - } - /** Tests the handling of stop command when soft AP is not started. */ @Test public void stopWhenNotStarted() throws Exception { @@ -198,31 +154,4 @@ public class SoftApManagerTest { verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0); verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); } - - /** Sends a broadcast intent indicating that the interface is available for tethering. */ - protected void announceAvailableForTethering() throws Exception { - when(mConnectivityManager.tether(TEST_INTERFACE_NAME)) - .thenReturn(ConnectivityManager.TETHER_ERROR_NO_ERROR); - ArrayList<String> availableList = - new ArrayList<String>(Arrays.asList(AVAILABLE_DEVICES)); - TestUtil.sendTetherStateChanged( - mBroadcastReceiver, mContext, availableList, new ArrayList<String>()); - mLooper.dispatchAll(); - } - - /** Verifies that tethering was requested. */ - protected void verifyTetheringRequested() throws Exception { - verify(mInterfaceConfiguration).setLinkAddress(any(LinkAddress.class)); - verify(mInterfaceConfiguration).setInterfaceUp(); - verify(mNmService).setInterfaceConfig(eq(TEST_INTERFACE_NAME), eq(mInterfaceConfiguration)); - } - - /** Sends a broadcast intent indicating that the interface is tethered. */ - protected void announceTethered() throws Exception { - ArrayList<String> deviceList = - new ArrayList<String>(Arrays.asList(AVAILABLE_DEVICES)); - TestUtil.sendTetherStateChanged( - mBroadcastReceiver, mContext, deviceList, deviceList); - mLooper.dispatchAll(); - } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index 145c84026..3993fe565 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import android.content.Context; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; @@ -155,6 +156,7 @@ public class WifiConfigStoreTest { + "}\n"; @Mock private WifiNative mWifiNative; + @Mock private Context mContext; private MockKeyStore mMockKeyStore; private WifiConfigStore mWifiConfigStore; @@ -163,8 +165,8 @@ public class WifiConfigStoreTest { MockitoAnnotations.initMocks(this); mMockKeyStore = new MockKeyStore(); - mWifiConfigStore = new WifiConfigStore(mWifiNative, mMockKeyStore.createMock(), null, - false, true); + mWifiConfigStore = new WifiConfigStore(mContext, mWifiNative, mMockKeyStore.createMock(), + null, false, true); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java index 2434c2db7..281ffa163 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java @@ -17,6 +17,7 @@ package com.android.server.wifi; import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; +import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -32,6 +33,7 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; import android.net.wifi.WifiScanner.PnoScanListener; import android.net.wifi.WifiScanner.PnoSettings; +import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanListener; import android.net.wifi.WifiScanner.ScanSettings; import android.net.wifi.WifiSsid; @@ -45,6 +47,7 @@ import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -69,11 +72,12 @@ public class WifiConnectivityManagerTest { mWifiStateMachine = mockWifiStateMachine(); mWifiConfigManager = mockWifiConfigManager(); mWifiInfo = getWifiInfo(); + mScanData = mockScanData(); mWifiScanner = mockWifiScanner(); mWifiQNS = mockWifiQualifiedNetworkSelector(); mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector, - mLooper.getLooper()); + mLooper.getLooper(), true); mWifiConnectivityManager.setWifiEnabled(true); when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime()); } @@ -94,6 +98,7 @@ public class WifiConnectivityManagerTest { private WifiQualifiedNetworkSelector mWifiQNS; private WifiStateMachine mWifiStateMachine; private WifiScanner mWifiScanner; + private ScanData mScanData; private WifiConfigManager mWifiConfigManager; private WifiInfo mWifiInfo; private Clock mClock = mock(Clock.class); @@ -126,12 +131,23 @@ public class WifiConnectivityManagerTest { return context; } + ScanData mockScanData() { + ScanData scanData = mock(ScanData.class); + + when(scanData.isAllChannelsScanned()).thenReturn(true); + + return scanData; + } + WifiScanner mockWifiScanner() { WifiScanner scanner = mock(WifiScanner.class); + ArgumentCaptor<ScanListener> allSingleScanListenerCaptor = + ArgumentCaptor.forClass(ScanListener.class); + + doNothing().when(scanner).registerScanListener(allSingleScanListenerCaptor.capture()); - // dummy scan results. QNS PeriodicScanListener bulids scanDetails from - // the fullScanResult and doesn't really use results - final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1]; + ScanData[] scanDatas = new ScanData[1]; + scanDatas[0] = mScanData; // do a synchronous answer for the ScanListener callbacks doAnswer(new AnswerWithArguments() { @@ -144,6 +160,7 @@ public class WifiConnectivityManagerTest { public void answer(ScanSettings settings, ScanListener listener, WorkSource workSource) throws Exception { listener.onResults(scanDatas); + allSingleScanListenerCaptor.getValue().onResults(scanDatas); }}).when(scanner).startScan(anyObject(), anyObject(), anyObject()); // This unfortunately needs to be a somewhat valid scan result, otherwise @@ -215,6 +232,7 @@ public class WifiConnectivityManagerTest { WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null); + when(wifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true); wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger( WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger( @@ -321,6 +339,30 @@ public class WifiConnectivityManagerTest { } /** + * Screen turned on while WiFi in connected state but + * auto roaming is disabled. + * + * Expected behavior: WifiConnectivityManager doesn't invoke + * WifiStateMachine.autoConnectToNetwork() because roaming + * is turned off. + */ + @Test + public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() { + // Set WiFi to connected state + mWifiConnectivityManager.handleConnectionStateChanged( + WifiConnectivityManager.WIFI_STATE_CONNECTED); + + // Turn off auto roaming + when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(false); + + // Set screen to on + mWifiConnectivityManager.handleScreenStateChanged(true); + + verify(mWifiStateMachine, times(0)).autoConnectToNetwork( + CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + } + + /** * Multiple back to back connection attempts within the rate interval should be rate limited. * * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() @@ -724,8 +766,9 @@ public class WifiConnectivityManagerTest { currentTimeStamp += 2000; when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp); - // Force a connectivity scan - mWifiConnectivityManager.forceConnectivityScan(); + // Allow untrusted networks so WifiConnectivityManager starts a periodic scan + // immediately. + mWifiConnectivityManager.setUntrustedConnectionAllowed(true); // Get the second periodic scan actual time stamp. Note, this scan is not // started from the AlarmManager. @@ -854,4 +897,99 @@ public class WifiConnectivityManagerTest { verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); } + + /** + * Verify that we retry connectivity scan up to MAX_SCAN_RESTART_ALLOWED times + * when Wifi somehow gets into a bad state and fails to scan. + * + * Expected behavior: WifiConnectivityManager schedules connectivity scan + * MAX_SCAN_RESTART_ALLOWED times. + */ + @Test + public void checkMaximumScanRetry() { + // Set screen to ON + mWifiConnectivityManager.handleScreenStateChanged(true); + + doAnswer(new AnswerWithArguments() { + public void answer(ScanSettings settings, ScanListener listener, + WorkSource workSource) throws Exception { + listener.onFailure(-1, "ScanFailure"); + }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject()); + + // Set WiFi to disconnected state to trigger the single scan based periodic scan + mWifiConnectivityManager.handleConnectionStateChanged( + WifiConnectivityManager.WIFI_STATE_DISCONNECTED); + + // Fire the alarm timer 2x timers + for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) { + mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG); + mLooper.dispatchAll(); + } + + // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED + // times. Note, WifiScanner.startScan() is invoked MAX_SCAN_RESTART_ALLOWED + 1 times. + // The very first scan is the initial one, and the other MAX_SCAN_RESTART_ALLOWED + // are the retrial ones. + verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 1)).startScan( + anyObject(), anyObject(), anyObject()); + } + + /** + * Listen to scan results not requested by WifiConnectivityManager and + * act on them. + * + * Expected behavior: WifiConnectivityManager calls + * WifiStateMachine.autoConnectToNetwork() with the + * expected candidate network ID and BSSID. + */ + @Test + public void listenToAllSingleScanResults() { + ScanSettings settings = new ScanSettings(); + ScanListener scanListener = mock(ScanListener.class); + + // Request a single scan outside of WifiConnectivityManager. + mWifiScanner.startScan(settings, scanListener, WIFI_WORK_SOURCE); + + // Verify that WCM receives the scan results and initiates a connection + // to the network. + verify(mWifiStateMachine).autoConnectToNetwork( + CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + } + + /** + * Verify that a forced connectivity scan waits for full band scan + * results. + * + * Expected behavior: WifiConnectivityManager doesn't invoke + * WifiStateMachine.autoConnectToNetwork() when full band scan + * results are not available. + */ + @Test + public void waitForFullBandScanResults() { + // Set WiFi to connected state. + mWifiConnectivityManager.handleConnectionStateChanged( + WifiConnectivityManager.WIFI_STATE_CONNECTED); + + // Set up as partial scan results. + when(mScanData.isAllChannelsScanned()).thenReturn(false); + + // Force a connectivity scan which enables WifiConnectivityManager + // to wait for full band scan results. + mWifiConnectivityManager.forceConnectivityScan(); + + // No roaming because no full band scan results. + verify(mWifiStateMachine, times(0)).autoConnectToNetwork( + CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + + // Set up as full band scan results. + when(mScanData.isAllChannelsScanned()).thenReturn(true); + + // Force a connectivity scan which enables WifiConnectivityManager + // to wait for full band scan results. + mWifiConnectivityManager.forceConnectivityScan(); + + // Roaming attempt because full band scan results are available. + verify(mWifiStateMachine).autoConnectToNetwork( + CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java index 44a710a05..c187faf27 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java @@ -16,9 +16,13 @@ package com.android.server.wifi; +import static android.net.wifi.WifiManager.WIFI_MODE_FULL; + import static com.android.server.wifi.WifiController.CMD_AP_STOPPED; +import static com.android.server.wifi.WifiController.CMD_DEVICE_IDLE; import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; +import static com.android.server.wifi.WifiController.CMD_RESTART_WIFI; import static com.android.server.wifi.WifiController.CMD_SET_AP; import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; @@ -28,6 +32,7 @@ import static org.mockito.Mockito.*; import android.content.ContentResolver; import android.content.Context; +import android.os.WorkSource; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; @@ -79,7 +84,7 @@ public class WifiControllerTest { @Mock FrameworkFacade mFacade; @Mock WifiSettingsStore mSettingsStore; @Mock WifiStateMachine mWifiStateMachine; - @Mock WifiServiceImpl.LockList mLockList; + @Mock WifiLockManager mWifiLockManager; WifiController mWifiController; @@ -94,7 +99,7 @@ public class WifiControllerTest { when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); mWifiController = new WifiController(mContext, mWifiStateMachine, - mSettingsStore, mLockList, mLooper.getLooper(), mFacade); + mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade); mWifiController.start(); mLooper.dispatchAll(); @@ -265,4 +270,184 @@ public class WifiControllerTest { inOrder.verify(mWifiStateMachine).setDriverStart(true); assertEquals("DeviceActiveState", getCurrentState().getName()); } + + /** + * When AP mode is enabled and wifi is toggled on, we should transition to + * DeviceActiveState after the AP is disabled. + * Enter DeviceActiveState, activate AP mode, toggle WiFi. + * <p> + * Expected: AP should successfully start and exit, then return to DeviceActiveState. + */ + @Test + public void testReturnToDeviceActiveStateAfterWifiEnabledShutdown() throws Exception { + enableWifi(); + assertEquals("DeviceActiveState", getCurrentState().getName()); + + mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); + mLooper.dispatchAll(); + assertEquals("ApEnabledState", getCurrentState().getName()); + + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); + mWifiController.obtainMessage(CMD_WIFI_TOGGLED).sendToTarget(); + mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); + mLooper.dispatchAll(); + + InOrder inOrder = inOrder(mWifiStateMachine); + inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); + inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); + inOrder.verify(mWifiStateMachine).setDriverStart(true); + assertEquals("DeviceActiveState", getCurrentState().getName()); + } + + /** + * When the wifi device is idle, AP mode is enabled and disabled + * we should return to the appropriate Idle state. + * Enter DeviceActiveState, indicate idle device, activate AP mode, disable AP mode. + * <p> + * Expected: AP should successfully start and exit, then return to a device idle state. + */ + @Test + public void testReturnToDeviceIdleStateAfterAPModeShutdown() throws Exception { + enableWifi(); + assertEquals("DeviceActiveState", getCurrentState().getName()); + + // make sure mDeviceIdle is set to true + when(mWifiLockManager.getStrongestLockMode()).thenReturn(WIFI_MODE_FULL); + when(mWifiLockManager.createMergedWorkSource()).thenReturn(new WorkSource()); + mWifiController.sendMessage(CMD_DEVICE_IDLE); + mLooper.dispatchAll(); + assertEquals("FullLockHeldState", getCurrentState().getName()); + + mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); + mLooper.dispatchAll(); + assertEquals("ApEnabledState", getCurrentState().getName()); + + when(mSettingsStore.getWifiSavedState()).thenReturn(1); + mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); + mLooper.dispatchAll(); + + InOrder inOrder = inOrder(mWifiStateMachine); + inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); + inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); + inOrder.verify(mWifiStateMachine).setDriverStart(true); + assertEquals("FullLockHeldState", getCurrentState().getName()); + } + + /** + * The command to trigger a WiFi reset should not trigger any action by WifiController if we + * are not in STA mode. + * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures + * should be ignored. + * Create and start WifiController in ApStaDisabledState, send command to restart WiFi + * <p> + * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false) + */ + @Test + public void testRestartWifiStackInApStaDisabledState() throws Exception { + // Start a new WifiController with wifi disabled + when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + + when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); + + mWifiController = new WifiController(mContext, mWifiStateMachine, + mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade); + + mWifiController.start(); + mLooper.dispatchAll(); + + reset(mWifiStateMachine); + assertEquals("ApStaDisabledState", getCurrentState().getName()); + mWifiController.sendMessage(CMD_RESTART_WIFI); + mLooper.dispatchAll(); + verifyZeroInteractions(mWifiStateMachine); + } + + /** + * The command to trigger a WiFi reset should not trigger any action by WifiController if we + * are not in STA mode, even if scans are allowed. + * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures + * should be ignored. + * Create and start WifiController in StaDisablediWithScanState, send command to restart WiFi + * <p> + * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false) + */ + @Test + public void testRestartWifiStackInStaDisabledWithScanState() throws Exception { + reset(mWifiStateMachine); + assertEquals("StaDisabledWithScanState", getCurrentState().getName()); + mWifiController.sendMessage(CMD_RESTART_WIFI); + mLooper.dispatchAll(); + verifyZeroInteractions(mWifiStateMachine); + } + + /** + * The command to trigger a WiFi reset should trigger a wifi reset in WifiStateMachine through + * the WifiStateMachine.setSupplicantRunning(false) call when in STA mode. + * WiFi is in connect mode, calls to reset the wifi stack due to connection failures + * should trigger a supplicant stop, and subsequently, a driver reload. + * Create and start WifiController in DeviceActiveState, send command to restart WiFi + * <p> + * Expected: WiFiController should call WifiStateMachine.setSupplicantRunning(false), + * WifiStateMachine should enter CONNECT_MODE and the wifi driver should be started. + */ + @Test + public void testRestartWifiStackInStaEnabledState() throws Exception { + enableWifi(); + + reset(mWifiStateMachine); + assertEquals("DeviceActiveState", getCurrentState().getName()); + mWifiController.sendMessage(CMD_RESTART_WIFI); + mLooper.dispatchAll(); + InOrder inOrder = inOrder(mWifiStateMachine); + inOrder.verify(mWifiStateMachine).setSupplicantRunning(false); + inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); + inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); + inOrder.verify(mWifiStateMachine).setDriverStart(true); + assertEquals("DeviceActiveState", getCurrentState().getName()); + } + + /** + * The command to trigger a WiFi reset should not trigger a reset when in ECM mode. + * Enable wifi and enter ECM state, send command to restart wifi. + * <p> + * Expected: The command to trigger a wifi reset should be ignored and we should remain in ECM + * mode. + */ + @Test + public void testRestartWifiStackDoesNotExitECMMode() throws Exception { + enableWifi(); + assertEquals("DeviceActiveState", getCurrentState().getName()); + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); + mLooper.dispatchAll(); + assertInEcm(true); + + reset(mWifiStateMachine); + mWifiController.sendMessage(CMD_RESTART_WIFI); + mLooper.dispatchAll(); + assertInEcm(true); + verifyZeroInteractions(mWifiStateMachine); + } + + /** + * The command to trigger a WiFi reset should not trigger a reset when in AP mode. + * Enter AP mode, send command to restart wifi. + * <p> + * Expected: The command to trigger a wifi reset should be ignored and we should remain in AP + * mode. + */ + @Test + public void testRestartWifiStackDoesNotExitAPMode() throws Exception { + mWifiController.obtainMessage(CMD_SET_AP, 1).sendToTarget(); + mLooper.dispatchAll(); + assertEquals("ApEnabledState", getCurrentState().getName()); + + reset(mWifiStateMachine); + mWifiController.sendMessage(CMD_RESTART_WIFI); + mLooper.dispatchAll(); + verifyZeroInteractions(mWifiStateMachine); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java index 2e62a309b..faa2f71f8 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java @@ -71,7 +71,7 @@ public class WifiCountryCodeTest { // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); verify(mWifiNative).setCountryCode(anyString()); - assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -81,13 +81,13 @@ public class WifiCountryCodeTest { @Test public void useTelephonyCountryCode() throws Exception { mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false); - assertEquals(null, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(null, mWifiCountryCode.getCountryCodeSentToDriver()); // Supplicant started. mWifiCountryCode.setReadyForChange(true); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); verify(mWifiNative).setCountryCode(anyString()); - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -98,13 +98,13 @@ public class WifiCountryCodeTest { public void setTelephonyCountryCodeAfterSupplicantStarts() throws Exception { // Supplicant starts. mWifiCountryCode.setReadyForChange(true); - assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); // Telephony country code arrives. mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); verify(mWifiNative, times(2)).setCountryCode(anyString()); - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -120,11 +120,11 @@ public class WifiCountryCodeTest { // Telephony country code arrives. mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false); // Telephony coutry code won't be applied at this time. - assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); mWifiCountryCode.setReadyForChange(true); // Telephony coutry is applied after supplicant is ready. verify(mWifiNative, times(2)).setCountryCode(anyString()); - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -138,15 +138,15 @@ public class WifiCountryCodeTest { mWifiCountryCode.setReadyForChange(true); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); // SIM card is removed. mWifiCountryCode.simCardRemoved(); // Country code restting is not applied yet. - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); mWifiCountryCode.setReadyForChange(true); // Country code restting is applied when supplicant is ready. verify(mWifiNative, times(2)).setCountryCode(anyString()); - assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -160,15 +160,15 @@ public class WifiCountryCodeTest { mWifiCountryCode.setReadyForChange(true); // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); // Airplane mode is enabled. mWifiCountryCode.simCardRemoved(); // Country code restting is not applied yet. - assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); mWifiCountryCode.setReadyForChange(true); // Country code restting is applied when supplicant is ready. verify(mWifiNative, times(2)).setCountryCode(anyString()); - assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } /** @@ -188,6 +188,6 @@ public class WifiCountryCodeTest { // Wifi get L2 connected. mWifiCountryCode.setReadyForChange(false); verify(mWifiNative).setCountryCode(anyString()); - assertEquals(persistentCountryCode, mWifiCountryCode.getCurrentCountryCode()); + assertEquals(persistentCountryCode, mWifiCountryCode.getCountryCodeSentToDriver()); } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java index 64f1ce0fd..08163e7f2 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.*; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiSsid; @@ -27,6 +28,7 @@ import android.util.Pair; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import java.util.ArrayList; import java.util.Arrays; @@ -38,7 +40,8 @@ import java.util.List; @SmallTest public class WifiLastResortWatchdogTest { WifiLastResortWatchdog mLastResortWatchdog; - WifiMetrics mWifiMetrics; + @Mock WifiMetrics mWifiMetrics; + @Mock WifiController mWifiController; private String[] mSsids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""}; private String[] mBssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55", "c0:ff:ee:ee:e3:ee"}; @@ -51,8 +54,9 @@ public class WifiLastResortWatchdogTest { @Before public void setUp() throws Exception { - mWifiMetrics = mock(WifiMetrics.class); + initMocks(this); mLastResortWatchdog = new WifiLastResortWatchdog(mWifiMetrics); + mLastResortWatchdog.setWifiController(mWifiController); } private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids, @@ -1276,6 +1280,8 @@ public class WifiLastResortWatchdogTest { ssids[ssids.length - 1], bssids[ssids.length - 1], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); assertEquals(true, watchdogTriggered); + verify(mWifiController).sendMessage(WifiController.CMD_RESTART_WIFI); + reset(mWifiController); } /** @@ -1434,7 +1440,7 @@ public class WifiLastResortWatchdogTest { int[] levels = {-60, -86, -50, -62, -60}; boolean[] isEphemeral = {false, false, false, false, false}; boolean[] hasEverConnected = {true, false, false, false, false}; - // Buffer potential candidates 1,2,3 & 4 + // Buffer potential candidates 1,2,3,4 & 5 List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids, bssids, frequencies, caps, levels, isEphemeral, hasEverConnected); mLastResortWatchdog.updateAvailableNetworks(candidates); @@ -1444,7 +1450,7 @@ public class WifiLastResortWatchdogTest { assertFailureCountEquals(bssids[i], 0, 0, 0); } - //Increment failure count for the first test network ssid & bssid + //Increment failure counts for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) { mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded( ssids[1], bssids[1], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); @@ -1472,6 +1478,58 @@ public class WifiLastResortWatchdogTest { verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggersWithBadAssociation(); verify(mWifiMetrics, times(1)).addCountToNumLastResortWatchdogBadDhcpNetworksTotal(3); verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggersWithBadDhcp(); + + // Simulate wifi connecting after triggering + mLastResortWatchdog.connectedStateTransition(true); + + // Verify that WifiMetrics counted this as a Watchdog success + verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses(); + + // Simulate wifi disconnecting + mLastResortWatchdog.connectedStateTransition(false); + + // Verify that WifiMetrics has still only counted one success + verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses(); + + // Remove the fifth network from candidates + candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, 4), + Arrays.copyOfRange(mBssids, 0, 4), + Arrays.copyOfRange(mFrequencies, 0, 4), + Arrays.copyOfRange(mCaps, 0, 4), + Arrays.copyOfRange(mLevels, 0, 4), + Arrays.copyOfRange(mIsEphemeral, 0, 4)); + + // Age out the fifth network + for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) { + mLastResortWatchdog.updateAvailableNetworks(candidates); + } + + //Increment failure counts + for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) { + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded( + ssids[1], bssids[1], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded( + ssids[2], bssids[2], WifiLastResortWatchdog.FAILURE_CODE_DHCP); + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded( + ssids[3], bssids[3], WifiLastResortWatchdog.FAILURE_CODE_DHCP); + mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded( + ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION); + } + + // Add network #5 back into the candidates + candidates = createFilteredQnsCandidates(ssids, + bssids, frequencies, caps, levels, isEphemeral, hasEverConnected); + + // LastResortWatchdog should reactivate because there is a new network (#5) available, + // Not because it was successful + mLastResortWatchdog.updateAvailableNetworks(candidates); + + // Simulate wifi connecting + mLastResortWatchdog.connectedStateTransition(true); + + // Verify that WifiMetrics did not count another success, as the connection could be due + // to the newly available network #5 + verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses(); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java new file mode 100644 index 000000000..1bbdda981 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java @@ -0,0 +1,320 @@ +/* + * 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 org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import android.content.Context; +import android.net.wifi.WifiManager; +import android.os.Binder; +import android.os.IBinder; +import android.os.WorkSource; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.internal.app.IBatteryStats; + +import org.junit.Before; +import org.junit.Test; + +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** Unit tests for {@link WifiLockManager}. */ +@SmallTest +public class WifiLockManagerTest { + + private static final int DEFAULT_TEST_UID_1 = 35; + private static final int DEFAULT_TEST_UID_2 = 53; + private static final int WIFI_LOCK_MODE_INVALID = -1; + private static final String TEST_WIFI_LOCK_TAG = "TestTag"; + + WifiLockManager mWifiLockManager; + @Mock IBatteryStats mBatteryStats; + @Mock IBinder mBinder; + WorkSource mWorkSource; + @Mock Context mContext; + + /** + * Method to setup a WifiLockManager for the tests. + * The WifiLockManager uses mocks for BatteryStats and Context. + */ + @Before + public void setUp() { + mWorkSource = new WorkSource(DEFAULT_TEST_UID_1); + MockitoAnnotations.initMocks(this); + mWifiLockManager = new WifiLockManager(mContext, mBatteryStats); + } + + private void acquireWifiLockSuccessful(int lockMode, String tag, IBinder binder, WorkSource ws) + throws Exception { + ArgumentCaptor<IBinder.DeathRecipient> deathRecipient = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + + assertTrue(mWifiLockManager.acquireWifiLock(lockMode, tag, binder, ws)); + assertThat(mWifiLockManager.getStrongestLockMode(), + not(WifiManager.WIFI_MODE_NO_LOCKS_HELD)); + InOrder inOrder = inOrder(binder, mBatteryStats); + inOrder.verify(binder).linkToDeath(deathRecipient.capture(), eq(0)); + inOrder.verify(mBatteryStats).noteFullWifiLockAcquiredFromSource(ws); + } + + private void releaseWifiLockSuccessful(IBinder binder) throws Exception { + ArgumentCaptor<IBinder.DeathRecipient> deathRecipient = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + + assertTrue(mWifiLockManager.releaseWifiLock(binder)); + InOrder inOrder = inOrder(binder, mBatteryStats); + inOrder.verify(binder).unlinkToDeath(deathRecipient.capture(), eq(0)); + inOrder.verify(mBatteryStats).noteFullWifiLockReleasedFromSource(any(WorkSource.class)); + } + + /** + * Test to check that a new WifiLockManager should not be holding any locks. + */ + @Test + public void newWifiLockManagerShouldNotHaveAnyLocks() { + assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + } + + /** + * Test to verify that the lock mode is verified before adding a lock. + * + * Steps: call acquireWifiLock with an invalid lock mode. + * Expected: the call should throw an IllegalArgumentException. + */ + @Test(expected = IllegalArgumentException.class) + public void acquireWifiLockShouldThrowExceptionOnInivalidLockMode() throws Exception { + mWifiLockManager.acquireWifiLock(WIFI_LOCK_MODE_INVALID, "", mBinder, mWorkSource); + } + + /** + * Test that a call to acquireWifiLock with valid parameters works. + * + * Steps: call acquireWifiLock on the empty WifiLockManager. + * Expected: A subsequent call to getStrongestLockMode should reflect the type of the lock we + * just added + */ + @Test + public void acquireWifiLockWithValidParamsShouldSucceed() throws Exception { + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL, "", mBinder, mWorkSource); + assertEquals(WifiManager.WIFI_MODE_FULL, mWifiLockManager.getStrongestLockMode()); + } + + /** + * Test that a call to acquireWifiLock will not succeed if there is already a lock for the same + * binder instance. + * + * Steps: call acquireWifiLock twice + * Expected: Second call should return false + */ + @Test + public void secondCallToAcquireWifiLockWithSameBinderShouldFail() throws Exception { + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_SCAN_ONLY, "", mBinder, mWorkSource); + assertEquals(WifiManager.WIFI_MODE_SCAN_ONLY, mWifiLockManager.getStrongestLockMode()); + assertFalse(mWifiLockManager.acquireWifiLock( + WifiManager.WIFI_MODE_SCAN_ONLY, "", mBinder, mWorkSource)); + } + + /** + * After acquiring a lock, we should be able to remove it. + * + * Steps: acquire a WifiLock and then remove it. + * Expected: Since a single lock was added, removing it should leave the WifiLockManager without + * any locks. We should not see any errors. + */ + @Test + public void releaseWifiLockShouldSucceed() throws Exception { + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL, "", mBinder, mWorkSource); + releaseWifiLockSuccessful(mBinder); + assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + } + + /** + * Releasing locks for one caller should not release locks for a different caller. + * + * Steps: acquire locks for two callers and remove locks for one. + * Expected: locks for remaining caller should still be active. + */ + @Test + public void releaseLocksForOneCallerNotImpactOtherCallers() throws Exception { + IBinder toReleaseBinder = mock(IBinder.class); + WorkSource toReleaseWS = new WorkSource(DEFAULT_TEST_UID_1); + WorkSource toKeepWS = new WorkSource(DEFAULT_TEST_UID_2); + + acquireWifiLockSuccessful( + WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", toReleaseBinder, toReleaseWS); + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_SCAN_ONLY, "", mBinder, toKeepWS); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_FULL_HIGH_PERF); + releaseWifiLockSuccessful(toReleaseBinder); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_SCAN_ONLY); + releaseWifiLockSuccessful(mBinder); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_NO_LOCKS_HELD); + } + + /** + * Attempting to release a lock that we do not hold should return false. + * + * Steps: release a WifiLock + * Expected: call to releaseWifiLock should return false. + */ + @Test + public void releaseWifiLockWithoutAcquireWillReturnFalse() { + assertFalse(mWifiLockManager.releaseWifiLock(mBinder)); + } + + /** + * Test used to verify call for getStrongestLockMode. + * + * Steps: The test first checks the return value for no held locks and then proceeds to test + * with a single lock of each type. + * Expected: getStrongestLockMode should reflect the type of lock we just added. + */ + @Test + public void checkForProperValueForGetStrongestLockMode() throws Exception { + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_NO_LOCKS_HELD); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_FULL_HIGH_PERF); + releaseWifiLockSuccessful(mBinder); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL, "", mBinder, mWorkSource); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_FULL); + releaseWifiLockSuccessful(mBinder); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_SCAN_ONLY, "", mBinder, mWorkSource); + assertEquals(mWifiLockManager.getStrongestLockMode(), WifiManager.WIFI_MODE_SCAN_ONLY); + } + + /** + * We should be able to create a merged WorkSource holding WorkSources for all active locks. + * + * Steps: call createMergedWorkSource and verify it is empty, add a lock and call again, it + * should have one entry. + * Expected: the first call should return a worksource with size 0 and the second should be size + * 1. + */ + @Test + public void createMergedWorkSourceShouldSucceed() throws Exception { + WorkSource checkMWS = mWifiLockManager.createMergedWorkSource(); + assertEquals(checkMWS.size(), 0); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource); + checkMWS = mWifiLockManager.createMergedWorkSource(); + assertEquals(checkMWS.size(), 1); + } + + /** + * Test the ability to update a WifiLock WorkSource with a new WorkSource. + * + * Steps: acquire a WifiLock with the default test worksource, then attempt to update it. + * Expected: Verify calls to release the original WorkSource and acquire with the new one to + * BatteryStats. + */ + @Test + public void testUpdateWifiLockWorkSourceCalledWithWorkSource() throws Exception { + WorkSource newWorkSource = new WorkSource(DEFAULT_TEST_UID_2); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource); + + mWifiLockManager.updateWifiLockWorkSource(mBinder, newWorkSource); + InOrder inOrder = inOrder(mBatteryStats); + inOrder.verify(mBatteryStats).noteFullWifiLockReleasedFromSource(mWorkSource); + inOrder.verify(mBatteryStats).noteFullWifiLockAcquiredFromSource(eq(newWorkSource)); + } + + /** + * Test the ability to update a WifiLock WorkSource with the callers UID. + * + * Steps: acquire a WifiLock with the default test worksource, then attempt to update it. + * Expected: Verify calls to release the original WorkSource and acquire with the new one to + * BatteryStats. + */ + @Test + public void testUpdateWifiLockWorkSourceCalledWithUID() throws Exception { + WorkSource newWorkSource = new WorkSource(Binder.getCallingUid()); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource); + + mWifiLockManager.updateWifiLockWorkSource(mBinder, null); + InOrder inOrder = inOrder(mBatteryStats); + inOrder.verify(mBatteryStats).noteFullWifiLockReleasedFromSource(mWorkSource); + inOrder.verify(mBatteryStats).noteFullWifiLockAcquiredFromSource(eq(newWorkSource)); + } + + /** + * Test an attempt to update a WifiLock that is not active. + * + * Steps: call updateWifiLockWorkSource + * Expected: catch an IllegalArgumentException + */ + @Test(expected = IllegalArgumentException.class) + public void testUpdateWifiLockWorkSourceCalledWithoutActiveLock() throws Exception { + mWifiLockManager.updateWifiLockWorkSource(mBinder, null); + } + + /** + * Verfies that dump() does not fail when no locks are held. + */ + @Test + public void dumpDoesNotFailWhenNoLocksAreHeld() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + mWifiLockManager.dump(pw); + + String wifiLockManagerDumpString = sw.toString(); + assertTrue(wifiLockManagerDumpString.contains( + "Locks acquired: 0 full, 0 full high perf, 0 scan")); + assertTrue(wifiLockManagerDumpString.contains( + "Locks released: 0 full, 0 full high perf, 0 scan")); + assertTrue(wifiLockManagerDumpString.contains("Locks held:")); + assertFalse(wifiLockManagerDumpString.contains("WifiLock{")); + } + + /** + * Verifies that dump() contains lock information when there are locks held. + */ + @Test + public void dumpOutputsCorrectInformationWithActiveLocks() throws Exception { + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource); + releaseWifiLockSuccessful(mBinder); + + acquireWifiLockSuccessful( + WifiManager.WIFI_MODE_FULL, TEST_WIFI_LOCK_TAG, mBinder, mWorkSource); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + mWifiLockManager.dump(pw); + + String wifiLockManagerDumpString = sw.toString(); + assertTrue(wifiLockManagerDumpString.contains( + "Locks acquired: 1 full, 1 full high perf, 0 scan")); + assertTrue(wifiLockManagerDumpString.contains( + "Locks released: 0 full, 1 full high perf, 0 scan")); + assertTrue(wifiLockManagerDumpString.contains("Locks held:")); + assertTrue(wifiLockManagerDumpString.contains( + "WifiLock{" + TEST_WIFI_LOCK_TAG + " type=" + WifiManager.WIFI_MODE_FULL + + " uid=" + Binder.getCallingUid() + "}")); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java index 55dc683c7..d915ff368 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java @@ -16,7 +16,10 @@ package com.android.server.wifi; +import android.content.Context; import android.test.suitebuilder.annotation.SmallTest; +import com.android.internal.R; +import android.util.LocalLog; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -52,11 +55,16 @@ public class WifiLoggerTest { @Mock WifiStateMachine mWsm; @Mock WifiNative mWifiNative; @Mock BuildProperties mBuildProperties; + @Mock Context mContext; WifiLogger mWifiLogger; private static final String FAKE_RING_BUFFER_NAME = "fake-ring-buffer"; - private WifiNative.RingBufferStatus mFakeRbs; + private static final int SMALL_RING_BUFFER_SIZE_KB = 32; + private static final int LARGE_RING_BUFFER_SIZE_KB = 1024; + private static final int BYTES_PER_KBYTE = 1024; + private LocalLog mWifiNativeLocalLog; + private WifiNative.RingBufferStatus mFakeRbs; /** * Returns the data that we would dump in a bug report, for our ring buffer. * @return a 2-D byte array, where the first dimension is the record number, and the second @@ -72,20 +80,29 @@ public class WifiLoggerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mFakeRbs = new WifiNative.RingBufferStatus(); mFakeRbs.name = FAKE_RING_BUFFER_NAME; - WifiNative.RingBufferStatus[] ringBufferStatuses = new WifiNative.RingBufferStatus[] { mFakeRbs }; + mWifiNativeLocalLog = new LocalLog(8192); when(mWifiNative.getRingBufferStatus()).thenReturn(ringBufferStatuses); when(mWifiNative.readKernelLog()).thenReturn(""); + when(mWifiNative.getLocalLog()).thenReturn(mWifiNativeLocalLog); when(mBuildProperties.isEngBuild()).thenReturn(false); when(mBuildProperties.isUserdebugBuild()).thenReturn(false); when(mBuildProperties.isUserBuild()).thenReturn(true); - mWifiLogger = new WifiLogger(mWsm, mWifiNative, mBuildProperties); + MockResources resources = new MockResources(); + resources.setInteger(R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb, + SMALL_RING_BUFFER_SIZE_KB); + resources.setInteger(R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb, + LARGE_RING_BUFFER_SIZE_KB); + when(mContext.getResources()).thenReturn(resources); + + mWifiLogger = new WifiLogger(mContext, mWsm, mWifiNative, mBuildProperties); mWifiNative.enableVerboseLogging(0); } @@ -197,7 +214,7 @@ public class WifiLoggerTest { final boolean verbosityToggle = false; mWifiLogger.startLogging(verbosityToggle); - final byte[] data = new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL]; + final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]; mWifiLogger.onRingBufferData(mFakeRbs, data); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); @@ -214,7 +231,7 @@ public class WifiLoggerTest { final boolean verbosityToggle = false; mWifiLogger.startLogging(verbosityToggle); - final byte[] data1 = new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL]; + final byte[] data1 = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]; final byte[] data2 = {1, 2, 3}; mWifiLogger.onRingBufferData(mFakeRbs, data1); mWifiLogger.onRingBufferData(mFakeRbs, data2); @@ -526,7 +543,7 @@ public class WifiLoggerTest { final boolean verbosityToggle = false; mWifiLogger.startLogging(verbosityToggle); mWifiLogger.onRingBufferData( - mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL + 1]); + mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(0, getLoggerRingBufferData().length); } @@ -540,7 +557,7 @@ public class WifiLoggerTest { when(mBuildProperties.isUserBuild()).thenReturn(false); mWifiLogger.startLogging(verbosityToggle); mWifiLogger.onRingBufferData( - mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL + 1]); + mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(0, getLoggerRingBufferData().length); } @@ -554,7 +571,7 @@ public class WifiLoggerTest { when(mBuildProperties.isUserBuild()).thenReturn(false); mWifiLogger.startLogging(verbosityToggle); mWifiLogger.onRingBufferData( - mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL + 1]); + mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(0, getLoggerRingBufferData().length); } @@ -564,7 +581,8 @@ public class WifiLoggerTest { public void ringBufferSizeIsLargeInVerboseMode() throws Exception { final boolean verbosityToggle = true; mWifiLogger.startLogging(verbosityToggle); - mWifiLogger.onRingBufferData(mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_LARGE]); + mWifiLogger.onRingBufferData( + mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(1, getLoggerRingBufferData().length); } @@ -574,7 +592,8 @@ public class WifiLoggerTest { public void startLoggingGrowsRingBuffersIfNeeded() throws Exception { mWifiLogger.startLogging(false /* verbose disabled */); mWifiLogger.startLogging(true /* verbose enabled */); - mWifiLogger.onRingBufferData(mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_LARGE]); + mWifiLogger.onRingBufferData( + mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(1, getLoggerRingBufferData().length); } @@ -584,7 +603,7 @@ public class WifiLoggerTest { public void startLoggingShrinksRingBuffersIfNeeded() throws Exception { mWifiLogger.startLogging(true /* verbose enabled */); mWifiLogger.onRingBufferData( - mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL + 1]); + mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]); // Existing data is nuked (too large). mWifiLogger.startLogging(false /* verbose disabled */); @@ -593,7 +612,7 @@ public class WifiLoggerTest { // New data must obey limit as well. mWifiLogger.onRingBufferData( - mFakeRbs, new byte[WifiLogger.RING_BUFFER_BYTE_LIMIT_SMALL + 1]); + mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]); mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE); assertEquals(0, getLoggerRingBufferData().length); } @@ -704,4 +723,17 @@ public class WifiLoggerTest { mWifiLogger.dump(new FileDescriptor(), pw, new String[]{}); assertFalse(sw.toString().contains(WifiLogger.FIRMWARE_DUMP_SECTION_HEADER)); } + + /** Verifies that the dump() includes contents of WifiNative's LocalLog. */ + @Test + public void dumpIncludesContentOfWifiNativeLocalLog() { + final String wifiNativeLogMessage = "This is a message"; + mWifiNativeLocalLog.log(wifiNativeLogMessage); + + mWifiLogger.startLogging(false /* verbose disabled */); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + mWifiLogger.dump(new FileDescriptor(), pw, new String[]{}); + assertTrue(sw.toString().contains(wifiNativeLogMessage)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 530b2180b..15a5327d6 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; +import android.net.NetworkAgent; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.test.suitebuilder.annotation.SmallTest; @@ -33,6 +34,8 @@ import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -104,15 +107,51 @@ public class WifiMetricsTest { mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes); } - @Test - public void dumpHumanReadable() throws Exception { + /** + * Gets the 'clean dump' proto bytes from mWifiMetrics & deserializes it into + * mDeserializedWifiMetrics + */ + public void cleanDumpProtoAndDeserialize() throws Exception { ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(stream); String[] args = new String[0]; + + when(mClock.elapsedRealtime()).thenReturn(TEST_RECORD_DURATION_MILLIS); + //Test proto dump, by passing in proto arg option + args = new String[]{WifiMetrics.PROTO_DUMP_ARG, WifiMetrics.CLEAN_DUMP_ARG}; mWifiMetrics.dump(null, writer, args); writer.flush(); - assertTrue("stream.toString().contains(\"WifiMetrics\")", - stream.toString().contains("WifiMetrics")); + String protoByteString = stream.toString(); + byte[] protoBytes = Base64.decode(protoByteString, Base64.DEFAULT); + mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes); + } + + /** Verifies that dump() includes the expected header */ + @Test + public void stateDumpIncludesHeader() throws Exception { + assertStringContains(getStateDump(), "WifiMetrics"); + } + + /** Verifies that dump() includes correct alert count when there are no alerts. */ + @Test + public void stateDumpAlertCountIsCorrectWithNoAlerts() throws Exception { + assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=()"); + } + + /** Verifies that dump() includes correct alert count when there is one alert. */ + @Test + public void stateDumpAlertCountIsCorrectWithOneAlert() throws Exception { + mWifiMetrics.incrementAlertReasonCount(1); + assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=(1,1)"); + } + + /** Verifies that dump() includes correct alert count when there are multiple alerts. */ + @Test + public void stateDumpAlertCountIsCorrectWithMultipleAlerts() throws Exception { + mWifiMetrics.incrementAlertReasonCount(1); + mWifiMetrics.incrementAlertReasonCount(1); + mWifiMetrics.incrementAlertReasonCount(16); + assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=(1,2),(16,1)"); } @Test @@ -126,6 +165,8 @@ public class WifiMetricsTest { private static final int NUM_OPEN_NETWORKS = 2; private static final int NUM_PERSONAL_NETWORKS = 3; private static final int NUM_ENTERPRISE_NETWORKS = 5; + private static final int NUM_HIDDEN_NETWORKS = 3; + private static final int NUM_PASSPOINT_NETWORKS = 4; private static final boolean TEST_VAL_IS_LOCATION_ENABLED = true; private static final boolean IS_SCANNING_ALWAYS_ENABLED = true; private static final int NUM_NEWTORKS_ADDED_BY_USER = 13; @@ -154,6 +195,53 @@ public class WifiMetricsTest { private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_AUTHENTICATION = 8; private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_DHCP = 9; private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER = 10; + private static final int NUM_LAST_RESORT_WATCHDOG_SUCCESSES = 5; + private static final int NUM_RSSI_LEVELS_TO_INCREMENT = 20; + private static final int FIRST_RSSI_LEVEL = -80; + private static final int NUM_OPEN_NETWORK_SCAN_RESULTS = 1; + private static final int NUM_PERSONAL_NETWORK_SCAN_RESULTS = 4; + private static final int NUM_ENTERPRISE_NETWORK_SCAN_RESULTS = 3; + private static final int NUM_HIDDEN_NETWORK_SCAN_RESULTS = 1; + private static final int NUM_HOTSPOT2_R1_NETWORK_SCAN_RESULTS = 1; + private static final int NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS = 2; + private static final int NUM_SCANS = 5; + private static final int NUM_TOTAL_SCAN_RESULTS = 8; + private static final int MIN_RSSI_LEVEL = -127; + private static final int MAX_RSSI_LEVEL = 0; + private static final int WIFI_SCORE_RANGE_MIN = 0; + private static final int NUM_WIFI_SCORES_TO_INCREMENT = 20; + private static final int WIFI_SCORE_RANGE_MAX = 60; + private static final int NUM_OUT_OF_BOUND_ENTRIES = 10; + + private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease, + String capabilities) { + ScanDetail mockScanDetail = mock(ScanDetail.class); + NetworkDetail mockNetworkDetail = mock(NetworkDetail.class); + ScanResult mockScanResult = mock(ScanResult.class); + when(mockScanDetail.getNetworkDetail()).thenReturn(mockNetworkDetail); + when(mockScanDetail.getScanResult()).thenReturn(mockScanResult); + when(mockNetworkDetail.isHiddenBeaconFrame()).thenReturn(hidden); + when(mockNetworkDetail.getHSRelease()).thenReturn(hSRelease); + mockScanResult.capabilities = capabilities; + return mockScanDetail; + } + + private List<ScanDetail> buildMockScanDetailList() { + List<ScanDetail> mockScanDetails = new ArrayList<ScanDetail>(); + mockScanDetails.add(buildMockScanDetail(true, null, "[ESS]")); + mockScanDetails.add(buildMockScanDetail(false, null, "[WPA2-PSK-CCMP][ESS]")); + mockScanDetails.add(buildMockScanDetail(false, null, "[WPA-PSK-CCMP]")); + mockScanDetails.add(buildMockScanDetail(false, null, "[WPA-PSK-CCMP]")); + mockScanDetails.add(buildMockScanDetail(false, null, "[WEP]")); + mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R2, + "[WPA-EAP-CCMP]")); + mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R2, + "[WPA2-EAP+FT/EAP-CCMP]")); + mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R1, + "[WPA-EAP-CCMP]")); + return mockScanDetails; + } + /** * Set simple metrics, increment others */ @@ -162,6 +250,8 @@ public class WifiMetricsTest { mWifiMetrics.setNumOpenNetworks(NUM_OPEN_NETWORKS); mWifiMetrics.setNumPersonalNetworks(NUM_PERSONAL_NETWORKS); mWifiMetrics.setNumEnterpriseNetworks(NUM_ENTERPRISE_NETWORKS); + mWifiMetrics.setNumHiddenNetworks(NUM_HIDDEN_NETWORKS); + mWifiMetrics.setNumPasspointNetworks(NUM_PASSPOINT_NETWORKS); mWifiMetrics.setNumNetworksAddedByUser(NUM_NEWTORKS_ADDED_BY_USER); mWifiMetrics.setNumNetworksAddedByApps(NUM_NEWTORKS_ADDED_BY_APPS); mWifiMetrics.setIsLocationEnabled(TEST_VAL_IS_LOCATION_ENABLED); @@ -236,6 +326,43 @@ public class WifiMetricsTest { for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER; i++) { mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadOther(); } + for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_SUCCESSES; i++) { + mWifiMetrics.incrementNumLastResortWatchdogSuccesses(); + } + for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) { + for (int j = 0; j <= i; j++) { + mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL + i); + } + } + for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) { + mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL - i); + } + for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) { + mWifiMetrics.incrementRssiPollRssiCount(MAX_RSSI_LEVEL + i); + } + // Test alert-reason clamping. + mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MIN - 1); + mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MAX + 1); + // Simple cases for alert reason. + mWifiMetrics.incrementAlertReasonCount(1); + mWifiMetrics.incrementAlertReasonCount(1); + mWifiMetrics.incrementAlertReasonCount(1); + mWifiMetrics.incrementAlertReasonCount(2); + List<ScanDetail> mockScanDetails = buildMockScanDetailList(); + for (int i = 0; i < NUM_SCANS; i++) { + mWifiMetrics.countScanResults(mockScanDetails); + } + for (int score = WIFI_SCORE_RANGE_MIN; score < NUM_WIFI_SCORES_TO_INCREMENT; score++) { + for (int offset = 0; offset <= score; offset++) { + mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN + score); + } + } + for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) { + mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN - i); + } + for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) { + mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MAX + i); + } } /** @@ -254,6 +381,8 @@ public class WifiMetricsTest { assertEquals("mDeserializedWifiMetrics.numNetworksAddedByUser " + "== NUM_NEWTORKS_ADDED_BY_USER", mDeserializedWifiMetrics.numNetworksAddedByUser, NUM_NEWTORKS_ADDED_BY_USER); + assertEquals(NUM_HIDDEN_NETWORKS, mDeserializedWifiMetrics.numHiddenNetworks); + assertEquals(NUM_PASSPOINT_NETWORKS, mDeserializedWifiMetrics.numPasspointNetworks); assertEquals("mDeserializedWifiMetrics.numNetworksAddedByApps " + "== NUM_NEWTORKS_ADDED_BY_APPS", mDeserializedWifiMetrics.numNetworksAddedByApps, NUM_NEWTORKS_ADDED_BY_APPS); @@ -311,8 +440,53 @@ public class WifiMetricsTest { mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadDhcp); assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER, mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadOther); + assertEquals(NUM_LAST_RESORT_WATCHDOG_SUCCESSES, + mDeserializedWifiMetrics.numLastResortWatchdogSuccesses); assertEquals(TEST_RECORD_DURATION_SEC, mDeserializedWifiMetrics.recordDurationSec); + for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) { + assertEquals(MIN_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi); + assertEquals(i + 1, mDeserializedWifiMetrics.rssiPollRssiCount[i].count); + } + StringBuilder sb_rssi = new StringBuilder(); + sb_rssi.append("Number of RSSIs = " + mDeserializedWifiMetrics.rssiPollRssiCount.length); + assertTrue(sb_rssi.toString(), (mDeserializedWifiMetrics.rssiPollRssiCount.length + <= (MAX_RSSI_LEVEL - MIN_RSSI_LEVEL + 1))); + assertEquals(2, mDeserializedWifiMetrics.alertReasonCount[0].count); // Clamped reasons. + assertEquals(3, mDeserializedWifiMetrics.alertReasonCount[1].count); + assertEquals(1, mDeserializedWifiMetrics.alertReasonCount[2].count); + assertEquals(3, mDeserializedWifiMetrics.alertReasonCount.length); + assertEquals(NUM_TOTAL_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numTotalScanResults); + assertEquals(NUM_OPEN_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numOpenNetworkScanResults); + assertEquals(NUM_PERSONAL_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numPersonalNetworkScanResults); + assertEquals(NUM_ENTERPRISE_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numEnterpriseNetworkScanResults); + assertEquals(NUM_HIDDEN_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numHiddenNetworkScanResults); + assertEquals(NUM_HOTSPOT2_R1_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numHotspot2R1NetworkScanResults); + assertEquals(NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS * NUM_SCANS, + mDeserializedWifiMetrics.numHotspot2R2NetworkScanResults); + assertEquals(NUM_SCANS, + mDeserializedWifiMetrics.numScans); + for (int score_index = 0; score_index < NUM_WIFI_SCORES_TO_INCREMENT; score_index++) { + assertEquals(WIFI_SCORE_RANGE_MIN + score_index, + mDeserializedWifiMetrics.wifiScoreCount[score_index].score); + assertEquals(score_index + 1, + mDeserializedWifiMetrics.wifiScoreCount[score_index].count); + } + StringBuilder sb_wifi_score = new StringBuilder(); + sb_wifi_score.append("Number of wifi_scores = " + + mDeserializedWifiMetrics.wifiScoreCount.length); + assertTrue(sb_wifi_score.toString(), (mDeserializedWifiMetrics.wifiScoreCount.length + <= (WIFI_SCORE_RANGE_MAX - WIFI_SCORE_RANGE_MIN + 1))); + StringBuilder sb_wifi_limits = new StringBuilder(); + sb_wifi_limits.append("Wifi Score limit is " + NetworkAgent.WIFI_BASE_SCORE + + ">= " + WIFI_SCORE_RANGE_MAX); + assertTrue(sb_wifi_limits.toString(), NetworkAgent.WIFI_BASE_SCORE <= WIFI_SCORE_RANGE_MAX); } /** @@ -450,6 +624,8 @@ public class WifiMetricsTest { dumpProtoAndDeserialize(); //Check there are only 3 connection events assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 4); + assertEquals(mDeserializedWifiMetrics.rssiPollRssiCount.length, 0); + assertEquals(mDeserializedWifiMetrics.alertReasonCount.length, 0); // Create 2 ConnectionEvents mWifiMetrics.startConnectionEvent(null, "BLUE", @@ -468,4 +644,35 @@ public class WifiMetricsTest { //Check there are only 2 connection events assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 2); } + + /** + * Tests that after setting metrics values they can be serialized and deserialized with the + * $ adb shell dumpsys wifi wifiMetricsProto clean + */ + @Test + public void testClearMetricsDump() throws Exception { + setAndIncrementMetrics(); + startAndEndConnectionEventSucceeds(); + cleanDumpProtoAndDeserialize(); + assertDeserializedMetricsCorrect(); + assertEquals("mDeserializedWifiMetrics.connectionEvent.length", + 2, mDeserializedWifiMetrics.connectionEvent.length); + } + + private void assertStringContains( + String actualString, String expectedSubstring) { + assertTrue("Expected text not found in: " + actualString, + actualString.contains(expectedSubstring)); + } + + private String getStateDump() { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(stream); + String[] args = new String[0]; + mWifiMetrics.dump(null, writer, args); + writer.flush(); + return stream.toString(); + } } + + diff --git a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java index 64fee84fc..1726e7d32 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java @@ -2233,4 +2233,96 @@ public class WifiQualifiedNetworkSelectorTest { assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator .BestCandidateType.NONE, evaluator.getBestCandidateType()); } + + /** + * Case #46 Choose 2.4GHz BSSID with stronger RSSI value over + * 5GHz BSSID with weaker RSSI value + * + * In this test. we simulate following scenario: + * Two APs are found in scan results + * BSSID1 is @ 5GHz with RSSI -82 + * BSSID2 is @ 2Ghz with RSSI -72 + * These two BSSIDs get exactly the same QNS score + * + * expect BSSID2 to be chosen as it has stronger RSSI value + */ + @Test + public void chooseStrongerRssiOver5GHz() { + String[] ssids = {"\"test1\"", "\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] frequencies = {5220, 2437}; + String[] caps = {"[ESS]", "[ESS]"}; + int[] levels = {-82, -72}; + int[] security = {SECURITY_NONE, SECURITY_NONE}; + + List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); + WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); + prepareConfigStore(savedConfigs); + + final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs); + when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork); + scanResultLinkConfiguration(savedConfigs, scanDetails); + + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + + WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, + false, scanDetails, false, false, true, false); + + verifySelectedResult(chosenScanResult, candidate); + } + + /** + * Case #47 Choose the currently connected BSSID after a firmware initiated roaming. + * + * In this test. we simulate following scenario: + * Two APs are found in scan results + * BSSID1 is @ 2.4GHz with RSSI -78 + * BSSID2 is @ 2.4Ghz with RSSI -77 + * BSSID2 is chosen because of stronger RSSI. Then firmware initiates + * a roaming to BSSID1. QNS now selects BSSID1 because of the bonus for currently + * connected network even if BSSID 2 has slightly stronger signal strengh. + * + * expect BSSID2 to be chosen after firmware roaming + */ + @Test + public void chooseCurrentlyConnectedBssid() { + String[] ssids = {"\"test1\"", "\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; + int[] frequencies = {2437, 2437}; + String[] caps = {"[ESS]", "[ESS]"}; + int[] levels = {-78, -77}; + int[] security = {SECURITY_NONE, SECURITY_NONE}; + + List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels); + WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security); + prepareConfigStore(savedConfigs); + + final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs); + when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork); + scanResultLinkConfiguration(savedConfigs, scanDetails); + + // Choose BSSID2 as it has stronger RSSI + ScanResult chosenScanResult = scanDetails.get(1).getScanResult(); + WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, + false, scanDetails, false, false, true, false); + verifySelectedResult(chosenScanResult, candidate); + when(mWifiInfo.getBSSID()).thenReturn(bssids[1]); + when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true); + + // Choose BSSID2 as it has stronger RSSI and it is the currently connected BSSID + chosenScanResult = scanDetails.get(1).getScanResult(); + candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true, + false, scanDetails, false, true, false, false); + verifySelectedResult(chosenScanResult, candidate); + + // Pretend firmware roamed the device to BSSID1 + when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); + + // Choose BSSID1 as it is the currently connected BSSID even if BSSID2 has slightly + // higher RSSI value. + chosenScanResult = scanDetails.get(0).getScanResult(); + candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true, + false, scanDetails, false, true, false, false); + verifySelectedResult(chosenScanResult, candidate); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java index e0f94ad17..a881d8f04 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import android.app.ActivityManager; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; @@ -41,6 +42,7 @@ import android.net.wifi.WifiSsid; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -56,6 +58,8 @@ import android.os.UserManager; import android.provider.Settings; import android.security.KeyStore; import android.telephony.TelephonyManager; +import android.test.mock.MockContentProvider; +import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.SmallTest; import android.util.Base64; import android.util.Log; @@ -218,13 +222,19 @@ public class WifiStateMachineTest { Context context = mock(Context.class); when(context.getPackageManager()).thenReturn(pkgMgr); - when(context.getContentResolver()).thenReturn(mock(ContentResolver.class)); MockResources resources = new com.android.server.wifi.MockResources(); when(context.getResources()).thenReturn(resources); - ContentResolver cr = mock(ContentResolver.class); - when(context.getContentResolver()).thenReturn(cr); + MockContentResolver mockContentResolver = new MockContentResolver(); + mockContentResolver.addProvider(Settings.AUTHORITY, + new MockContentProvider(context) { + @Override + public Bundle call(String method, String arg, Bundle extras) { + return new Bundle(); + } + }); + when(context.getContentResolver()).thenReturn(mockContentResolver); when(context.getSystemService(Context.POWER_SERVICE)).thenReturn( new PowerManager(context, mock(IPowerManager.class), new Handler())); @@ -475,6 +485,75 @@ public class WifiStateMachineTest { assertEquals("InitialState", getCurrentState().getName()); } + /** + * Test to check that mode changes from WifiController will be properly handled in the + * InitialState by WifiStateMachine. + */ + @Test + public void checkOperationalModeInInitialState() throws Exception { + when(mWifiNative.loadDriver()).thenReturn(true); + when(mWifiNative.startHal()).thenReturn(true); + when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true); + + mLooper.dispatchAll(); + assertEquals("InitialState", getCurrentState().getName()); + assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest()); + + mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE); + mLooper.dispatchAll(); + assertEquals(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE, + mWsm.getOperationalModeForTest()); + + mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE); + mLooper.dispatchAll(); + assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest()); + + mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); + mLooper.dispatchAll(); + assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest()); + } + + /** + * Test that mode changes for WifiStateMachine in the InitialState are realized when supplicant + * is started. + */ + @Test + public void checkStartInCorrectStateAfterChangingInitialState() throws Exception { + when(mWifiNative.loadDriver()).thenReturn(true); + when(mWifiNative.startHal()).thenReturn(true); + when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true); + + // Check initial state + mLooper.dispatchAll(); + assertEquals("InitialState", getCurrentState().getName()); + assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest()); + + // Update the mode + mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE); + mLooper.dispatchAll(); + assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest()); + + // Start supplicant so we move to the next state + mWsm.setSupplicantRunning(true); + mLooper.dispatchAll(); + assertEquals("SupplicantStartingState", getCurrentState().getName()); + when(mWifiNative.setBand(anyInt())).thenReturn(true); + when(mWifiNative.setDeviceName(anyString())).thenReturn(true); + when(mWifiNative.setManufacturer(anyString())).thenReturn(true); + when(mWifiNative.setModelName(anyString())).thenReturn(true); + when(mWifiNative.setModelNumber(anyString())).thenReturn(true); + when(mWifiNative.setSerialNumber(anyString())).thenReturn(true); + when(mWifiNative.setConfigMethods(anyString())).thenReturn(true); + when(mWifiNative.setDeviceType(anyString())).thenReturn(true); + when(mWifiNative.setSerialNumber(anyString())).thenReturn(true); + when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true); + + mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT); + mLooper.dispatchAll(); + + assertEquals("ScanModeState", getCurrentState().getName()); + } + private void addNetworkAndVerifySuccess() throws Exception { addNetworkAndVerifySuccess(false); } diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java index 1355961c1..767cddfea 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java @@ -89,6 +89,12 @@ public abstract class BaseWifiScannerImplTest { when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime()); } + protected boolean isAllChannelsScanned(int band) { + ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection(); + collection.addBand(band); + return collection.isAllChannels(); + } + protected Set<Integer> expectedBandScanFreqs(int band) { ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection(); collection.addBand(band); @@ -115,7 +121,8 @@ public abstract class BaseWifiScannerImplTest { doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(), - ScanResults.create(0, 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), false); + ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ), + 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), false); } @Test @@ -144,7 +151,8 @@ public abstract class BaseWifiScannerImplTest { doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(), - ScanResults.create(0, 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), true); + ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ), + 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), true); } /** @@ -346,7 +354,7 @@ public abstract class BaseWifiScannerImplTest { .withBasePeriod(10000) .withMaxApPerScan(10) .addBucketWithBand(10000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, - WifiScanner.WIFI_BAND_5_GHZ) + WifiScanner.WIFI_BAND_BOTH_WITH_DFS) .build(); WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); @@ -361,15 +369,17 @@ public abstract class BaseWifiScannerImplTest { expectSuccessfulSingleScan(order, eventHandler, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(), - ScanResults.create(0, 2400, 2450, 2450), false); + ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ), + 2400, 2450, 2450), false); // start second scan assertTrue(mScanner.startSingleScan(settings2, eventHandler)); expectSuccessfulSingleScan(order, eventHandler, - expectedBandScanFreqs(WifiScanner.WIFI_BAND_5_GHZ), + expectedBandScanFreqs(WifiScanner.WIFI_BAND_BOTH_WITH_DFS), new HashSet<Integer>(), - ScanResults.create(0, 5150, 5175), false); + ScanResults.create(0, true, + 5150, 5175), false); verifyNoMoreInteractions(eventHandler); } @@ -412,7 +422,8 @@ public abstract class BaseWifiScannerImplTest { } ArrayList<ScanResult> scanDataResults = new ArrayList<>(fullResults); Collections.sort(scanDataResults, ScanResults.SCAN_RESULT_RSSI_COMPARATOR); - ScanData scanData = new ScanData(0, 0, + ScanData scanData = new ScanData(0, 0, 0, + isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ), scanDataResults.toArray(new ScanResult[scanDataResults.size()])); Set<Integer> expectedScan = expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ); diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java index ed466835d..3e482a9fe 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java @@ -290,6 +290,7 @@ public class KnownBandsChannelHelperTest { assertTrue(mChannelCollection.isEmpty()); assertFalse(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -310,6 +311,7 @@ public class KnownBandsChannelHelperTest { assertTrue(mChannelCollection.isEmpty()); assertFalse(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -329,6 +331,7 @@ public class KnownBandsChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -348,6 +351,7 @@ public class KnownBandsChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -368,6 +372,7 @@ public class KnownBandsChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -388,6 +393,7 @@ public class KnownBandsChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -408,6 +414,7 @@ public class KnownBandsChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -428,6 +435,7 @@ public class KnownBandsChannelHelperTest { assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); assertTrue(mChannelCollection.containsChannel(5600)); + assertTrue(mChannelCollection.isAllChannels()); } /** @@ -442,6 +450,7 @@ public class KnownBandsChannelHelperTest { WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); mChannelCollection.fillBucketSettings(bucketSettings, 2); assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -456,6 +465,27 @@ public class KnownBandsChannelHelperTest { WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); mChannelCollection.fillBucketSettings(bucketSettings, 2); assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_BOTH)); + assertFalse(mChannelCollection.isAllChannels()); + } + + + /** + * Add enough channels across all bands that the max channels is exceeded + */ + @Test + public void addChannel_addAllAvailableChannels() { + mChannelCollection.addChannel(2400); + mChannelCollection.addChannel(2450); + mChannelCollection.addChannel(5150); + mChannelCollection.addChannel(5175); + mChannelCollection.addChannel(5600); + mChannelCollection.addChannel(5650); + mChannelCollection.addChannel(5660); + + WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); + mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE); + assertThat(bucketSettings, channelsAre(2400, 2450, 5150, 5175, 5600, 5650, 5660)); + assertTrue(mChannelCollection.isAllChannels()); } } } diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java index cbf6fe5d1..2863b9f90 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java @@ -199,7 +199,7 @@ public class NoBandChannelHelperTest { * {@link com.android.server.wifi.scanner.NoBandChannelHelper.NoBandChannelCollection}. */ @SmallTest - public static class KnownBandsChannelCollectionTest { + public static class NoBandsChannelCollectionTest { ChannelHelper.ChannelCollection mChannelCollection; /** @@ -225,6 +225,7 @@ public class NoBandChannelHelperTest { assertTrue(mChannelCollection.isEmpty()); assertFalse(mChannelCollection.containsChannel(2400)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -244,6 +245,7 @@ public class NoBandChannelHelperTest { assertTrue(mChannelCollection.isEmpty()); assertFalse(mChannelCollection.containsChannel(2400)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -262,6 +264,7 @@ public class NoBandChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); + assertTrue(mChannelCollection.isAllChannels()); } /** @@ -281,6 +284,7 @@ public class NoBandChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -301,6 +305,7 @@ public class NoBandChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertFalse(mChannelCollection.containsChannel(5150)); + assertFalse(mChannelCollection.isAllChannels()); } /** @@ -320,6 +325,7 @@ public class NoBandChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); + assertTrue(mChannelCollection.isAllChannels()); } /** @@ -339,6 +345,7 @@ public class NoBandChannelHelperTest { assertFalse(mChannelCollection.isEmpty()); assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); + assertTrue(mChannelCollection.isAllChannels()); } /** @@ -359,6 +366,7 @@ public class NoBandChannelHelperTest { assertTrue(mChannelCollection.containsChannel(2400)); assertTrue(mChannelCollection.containsChannel(5150)); assertTrue(mChannelCollection.containsChannel(5600)); + assertTrue(mChannelCollection.isAllChannels()); } /** @@ -373,6 +381,7 @@ public class NoBandChannelHelperTest { WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); mChannelCollection.fillBucketSettings(bucketSettings, 2); assertThat(bucketSettings, bandIs(ALL_BANDS)); + assertFalse(mChannelCollection.isAllChannels()); // can't determine from just channels } /** @@ -387,6 +396,7 @@ public class NoBandChannelHelperTest { WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); mChannelCollection.fillBucketSettings(bucketSettings, 2); assertThat(bucketSettings, bandIs(ALL_BANDS)); + assertFalse(mChannelCollection.isAllChannels()); // can't determine from just channels } } } diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java index 39709f84d..560710f79 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java @@ -45,6 +45,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -90,7 +91,7 @@ public class SupplicantPnoScannerTest { WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); - ScanResults scanResults = createDummyScanResults(); + ScanResults scanResults = createDummyScanResults(false); InOrder order = inOrder(pnoEventHandler, mWifiNative); // Start PNO scan @@ -111,7 +112,7 @@ public class SupplicantPnoScannerTest { WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); WifiNative.ScanSettings settings = createDummyScanSettings(); - ScanResults scanResults = createDummyScanResults(); + ScanResults scanResults = createDummyScanResults(true); InOrder order = inOrder(eventHandler, mWifiNative); // Start PNO scan @@ -181,7 +182,7 @@ public class SupplicantPnoScannerTest { WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); WifiNative.ScanSettings settings = createDummyScanSettings(); - ScanResults scanResults = createDummyScanResults(); + ScanResults scanResults = createDummyScanResults(true); InOrder order = inOrder(eventHandler, mWifiNative); // Start PNO scan @@ -214,12 +215,126 @@ public class SupplicantPnoScannerTest { verifyNoMoreInteractions(pnoEventHandler); } + /** + * Verify that the HW PNO scan stop failure still resets the PNO scan state. + * 1. Start Hw PNO. + * 2. Stop Hw PNO scan which raises a stop command to WifiNative which is failed. + * 3. Now restart a new PNO scan to ensure that the failure was cleanly handled. + */ + @Test + public void ignoreHwDisconnectedPnoScanStopFailure() { + createScannerWithHwPnoScanSupport(); + + WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); + WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); + + // Start PNO scan + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + + // Fail the PNO stop. + when(mWifiNative.setPnoScan(false)).thenReturn(false); + assertTrue(mScanner.resetHwPnoList()); + assertTrue("dispatch pno monitor alarm", + mAlarmManager.dispatch( + SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); + mLooper.dispatchAll(); + verify(mWifiNative).setPnoScan(false); + + // Add a new PNO scan request and ensure it runs successfully. + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + assertTrue("dispatch pno monitor alarm", + mAlarmManager.dispatch( + SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); + mLooper.dispatchAll(); + InOrder order = inOrder(pnoEventHandler, mWifiNative); + ScanResults scanResults = createDummyScanResults(false); + expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults); + verifyNoMoreInteractions(pnoEventHandler); + } + + /** + * Verify that the HW PNO scan is forcefully stopped (bypass debounce logic) and restarted when + * settings change. + * 1. Start Hw PNO. + * 2. Stop Hw PNO . + * 3. Now restart a new PNO scan with different settings. + * 4. Ensure that the stop was issued before we start again. + */ + @Test + public void forceRestartHwDisconnectedPnoScanWhenSettingsChange() { + createScannerWithHwPnoScanSupport(); + + WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); + WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); + InOrder order = inOrder(pnoEventHandler, mWifiNative); + + // Start PNO scan + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + expectHwDisconnectedPnoScanStart(order, pnoSettings); + + // Stop PNO now. This should trigger the debounce timer and not stop PNO. + assertTrue(mScanner.resetHwPnoList()); + assertTrue(mAlarmManager.isPending( + SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); + order.verify(mWifiNative, never()).setPnoScan(false); + + // Now restart PNO scan with an extra network in settings. + pnoSettings.networkList = + Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1); + pnoSettings.networkList[pnoSettings.networkList.length - 1] = + createDummyPnoNetwork("ssid_pno_new", 6, 6); + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + + // This should bypass the debounce timer and stop PNO scan immediately and then start + // a new debounce timer for the start. + order.verify(mWifiNative).setPnoScan(false); + + // Trigger the debounce timer and ensure we start PNO scan again. + mAlarmManager.dispatch(SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG); + mLooper.dispatchAll(); + order.verify(mWifiNative).setPnoScan(true); + } + + /** + * Verify that the HW PNO scan is not forcefully stopped (bypass debounce logic) when + * settings don't change. + * 1. Start Hw PNO. + * 2. Stop Hw PNO . + * 3. Now restart a new PNO scan with same settings. + * 4. Ensure that the stop was never issued. + */ + @Test + public void noForceRestartHwDisconnectedPnoScanWhenNoSettingsChange() { + createScannerWithHwPnoScanSupport(); + + WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); + WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false); + InOrder order = inOrder(pnoEventHandler, mWifiNative); + + // Start PNO scan + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + expectHwDisconnectedPnoScanStart(order, pnoSettings); + + // Stop PNO now. This should trigger the debounce timer and not stop PNO. + assertTrue(mScanner.resetHwPnoList()); + assertTrue(mAlarmManager.isPending( + SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG)); + order.verify(mWifiNative, never()).setPnoScan(false); + + // Now restart PNO scan with the same settings. + startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler); + + // Trigger the debounce timer and ensure that we neither stop/start. + mLooper.dispatchAll(); + order.verify(mWifiNative, never()).setPnoScan(anyBoolean()); + } + private void doSuccessfulSwPnoScanTest(boolean isConnectedPno) { WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class); WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(isConnectedPno); WifiNative.ScanEventHandler scanEventHandler = mock(WifiNative.ScanEventHandler.class); WifiNative.ScanSettings scanSettings = createDummyScanSettings(); - ScanResults scanResults = createDummyScanResults(); + ScanResults scanResults = createDummyScanResults(false); InOrder order = inOrder(scanEventHandler, mWifiNative); @@ -243,18 +358,20 @@ public class SupplicantPnoScannerTest { new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock); } + private WifiNative.PnoNetwork createDummyPnoNetwork(String ssid, int networkId, int priority) { + WifiNative.PnoNetwork pnoNetwork = new WifiNative.PnoNetwork(); + pnoNetwork.ssid = ssid; + pnoNetwork.networkId = networkId; + pnoNetwork.priority = priority; + return pnoNetwork; + } + private WifiNative.PnoSettings createDummyPnoSettings(boolean isConnected) { WifiNative.PnoSettings pnoSettings = new WifiNative.PnoSettings(); pnoSettings.isConnected = isConnected; pnoSettings.networkList = new WifiNative.PnoNetwork[2]; - pnoSettings.networkList[0] = new WifiNative.PnoNetwork(); - pnoSettings.networkList[0].ssid = "ssid_pno_1"; - pnoSettings.networkList[0].networkId = 1; - pnoSettings.networkList[0].priority = 1; - pnoSettings.networkList[1] = new WifiNative.PnoNetwork(); - pnoSettings.networkList[1].ssid = "ssid_pno_2"; - pnoSettings.networkList[1].networkId = 2; - pnoSettings.networkList[1].priority = 2; + pnoSettings.networkList[0] = createDummyPnoNetwork("ssid_pno_1", 1, 1); + pnoSettings.networkList[1] = createDummyPnoNetwork("ssid_pno_2", 2, 2); return pnoSettings; } @@ -268,13 +385,15 @@ public class SupplicantPnoScannerTest { return settings; } - private ScanResults createDummyScanResults() { - return ScanResults.create(0, 2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450); + private ScanResults createDummyScanResults(boolean allChannelsScanned) { + return ScanResults.create(0, allChannelsScanned, 2400, 2450, 2450, 2400, 2450, 2450, 2400, + 2450, 2450); } private void startSuccessfulPnoScan(WifiNative.ScanSettings scanSettings, WifiNative.PnoSettings pnoSettings, WifiNative.ScanEventHandler scanEventHandler, WifiNative.PnoEventHandler pnoEventHandler) { + reset(mWifiNative); when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true); when(mWifiNative.enableNetworkWithoutConnect(anyInt())).thenReturn(true); // Scans succeed @@ -300,9 +419,8 @@ public class SupplicantPnoScannerTest { /** * Verify that the PNO scan was successfully started. */ - private void expectSuccessfulHwDisconnectedPnoScan(InOrder order, - WifiNative.PnoSettings pnoSettings, WifiNative.PnoEventHandler eventHandler, - ScanResults scanResults) { + private void expectHwDisconnectedPnoScanStart(InOrder order, + WifiNative.PnoSettings pnoSettings) { for (int i = 0; i < pnoSettings.networkList.length; i++) { WifiNative.PnoNetwork network = pnoSettings.networkList[i]; order.verify(mWifiNative).setNetworkVariable(network.networkId, @@ -311,6 +429,17 @@ public class SupplicantPnoScannerTest { } // Verify HW PNO scan started order.verify(mWifiNative).setPnoScan(true); + } + + /** + * + * 1. Verify that the PNO scan was successfully started. + * 2. Send scan results and ensure that the |onPnoNetworkFound| callback was called. + */ + private void expectSuccessfulHwDisconnectedPnoScan(InOrder order, + WifiNative.PnoSettings pnoSettings, WifiNative.PnoEventHandler eventHandler, + ScanResults scanResults) { + expectHwDisconnectedPnoScanStart(order, pnoSettings); // Setup scan results when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList()); diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index 62f510526..3498b53fc 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.util.Pair; import com.android.internal.app.IBatteryStats; +import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.server.wifi.BidirectionalAsyncChannel; import com.android.server.wifi.Clock; @@ -137,13 +138,13 @@ public class WifiScanningServiceTest { return controlChannel; } - private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler) { + private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler) { ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); order.verify(handler).handleMessage(messageCaptor.capture()); return messageCaptor.getValue(); } - private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler, + private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler, final int what) { CapturingMatcher<Message> messageMatcher = new CapturingMatcher<Message>() { public boolean matches(Object argument) { @@ -155,14 +156,14 @@ public class WifiScanningServiceTest { return messageMatcher.getLastValue(); } - private void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, + private static void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, WifiScanner.ScanData... expected) { Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler, WifiScanner.CMD_SCAN_RESULT); assertScanResultsMessage(listenerId, expected, scanResultMessage); } - private void assertScanResultsMessage(int listenerId, WifiScanner.ScanData[] expected, + private static void assertScanResultsMessage(int listenerId, WifiScanner.ScanData[] expected, Message scanResultMessage) { assertEquals("what", WifiScanner.CMD_SCAN_RESULT, scanResultMessage.what); assertEquals("listenerId", listenerId, scanResultMessage.arg2); @@ -170,18 +171,19 @@ public class WifiScanningServiceTest { ((WifiScanner.ParcelableScanData) scanResultMessage.obj).getResults()); } - private void verifySingleScanCompletedRecieved(InOrder order, Handler handler, int listenerId) { + private static void verifySingleScanCompletedRecieved(InOrder order, Handler handler, + int listenerId) { Message completedMessage = verifyHandleMessageAndGetMessage(order, handler, WifiScanner.CMD_SINGLE_SCAN_COMPLETED); assertSingleScanCompletedMessage(listenerId, completedMessage); } - private void assertSingleScanCompletedMessage(int listenerId, Message completedMessage) { + private static void assertSingleScanCompletedMessage(int listenerId, Message completedMessage) { assertEquals("what", WifiScanner.CMD_SINGLE_SCAN_COMPLETED, completedMessage.what); assertEquals("listenerId", listenerId, completedMessage.arg2); } - private void sendBackgroundScanRequest(BidirectionalAsyncChannel controlChannel, + private static void sendBackgroundScanRequest(BidirectionalAsyncChannel controlChannel, int scanRequestId, WifiScanner.ScanSettings settings, WorkSource workSource) { Bundle scanParams = new Bundle(); scanParams.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); @@ -190,7 +192,7 @@ public class WifiScanningServiceTest { scanRequestId, scanParams)); } - private void sendSingleScanRequest(BidirectionalAsyncChannel controlChannel, + private static void sendSingleScanRequest(BidirectionalAsyncChannel controlChannel, int scanRequestId, WifiScanner.ScanSettings settings, WorkSource workSource) { Bundle scanParams = new Bundle(); scanParams.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings); @@ -199,12 +201,24 @@ public class WifiScanningServiceTest { scanRequestId, scanParams)); } - private void verifySuccessfulResponse(InOrder order, Handler handler, int arg2) { + private static void registerScanListener(BidirectionalAsyncChannel controlChannel, + int listenerRequestId) { + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_REGISTER_SCAN_LISTENER, 0, + listenerRequestId, null)); + } + + private static void deregisterScanListener(BidirectionalAsyncChannel controlChannel, + int listenerRequestId) { + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_DEREGISTER_SCAN_LISTENER, 0, + listenerRequestId, null)); + } + + private static void verifySuccessfulResponse(InOrder order, Handler handler, int arg2) { Message response = verifyHandleMessageAndGetMessage(order, handler); assertSuccessfulResponse(arg2, response); } - private void assertSuccessfulResponse(int arg2, Message response) { + private static void assertSuccessfulResponse(int arg2, Message response) { if (response.what == WifiScanner.CMD_OP_FAILED) { WifiScanner.OperationResult result = (WifiScanner.OperationResult) response.obj; fail("response indicates failure, reason=" + result.reason @@ -215,13 +229,65 @@ public class WifiScanningServiceTest { } } - private void verifyFailedResponse(InOrder order, Handler handler, int arg2, + /** + * If multiple results are expected for a single hardware scan then the order that they are + * dispatched is dependant on the order which they are iterated through internally. This + * function validates that the order is either one way or the other. A scan listener can + * optionally be provided as well and will be checked after the after the single scan requests. + */ + private static void verifyMultipleSingleScanResults(InOrder handlerOrder, Handler handler, + int requestId1, ScanResults results1, int requestId2, ScanResults results2, + int listenerRequestId, ScanResults listenerResults) { + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + handlerOrder.verify(handler, times(listenerResults == null ? 4 : 5)) + .handleMessage(messageCaptor.capture()); + int firstListenerId = messageCaptor.getAllValues().get(0).arg2; + assertTrue(firstListenerId + " was neither " + requestId2 + " nor " + requestId1, + firstListenerId == requestId2 || firstListenerId == requestId1); + if (firstListenerId == requestId2) { + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId1, + new WifiScanner.ScanData[] {results1.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId1, messageCaptor.getAllValues().get(3)); + if (listenerResults != null) { + assertScanResultsMessage(listenerRequestId, + new WifiScanner.ScanData[] {listenerResults.getScanData()}, + messageCaptor.getAllValues().get(4)); + } + } else { + assertScanResultsMessage(requestId1, + new WifiScanner.ScanData[] {results1.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId1, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(3)); + if (listenerResults != null) { + assertScanResultsMessage(listenerRequestId, + new WifiScanner.ScanData[] {listenerResults.getScanData()}, + messageCaptor.getAllValues().get(4)); + } + } + } + + private static void verifyMultipleSingleScanResults(InOrder handlerOrder, Handler handler, + int requestId1, ScanResults results1, int requestId2, ScanResults results2) { + verifyMultipleSingleScanResults(handlerOrder, handler, requestId1, results1, requestId2, + results2, -1, null); + } + + private static void verifyFailedResponse(InOrder order, Handler handler, int arg2, int expectedErrorReason, String expectedErrorDescription) { Message response = verifyHandleMessageAndGetMessage(order, handler); assertFailedResponse(arg2, expectedErrorReason, expectedErrorDescription, response); } - private void assertFailedResponse(int arg2, int expectedErrorReason, + private static void assertFailedResponse(int arg2, int expectedErrorReason, String expectedErrorDescription, Message response) { if (response.what == WifiScanner.CMD_OP_SUCCEEDED) { fail("response indicates success"); @@ -299,22 +365,24 @@ public class WifiScanningServiceTest { private void assertDumpContainsRequestLog(String type, int id) { String serviceDump = dumpService(); - Pattern logLineRegex = Pattern.compile("^.+" + type + ": ClientInfo\\[uid=\\d+\\],Id=" + - id + ".*$", Pattern.MULTILINE); + Pattern logLineRegex = Pattern.compile("^.+" + type + + ": ClientInfo\\[uid=\\d+,android\\.os\\.Messenger@[a-f0-9]+\\],Id=" + id + + ".*$", Pattern.MULTILINE); assertTrue("dump did not contain log with type=" + type + ", id=" + id + ": " + serviceDump + "\n", logLineRegex.matcher(serviceDump).find()); - } + } private void assertDumpContainsCallbackLog(String callback, int id, String extra) { String serviceDump = dumpService(); String extraPattern = extra == null ? "" : "," + extra; - Pattern logLineRegex = Pattern.compile("^.+" + callback + ": ClientInfo\\[uid=\\d+\\],Id=" + - id + extraPattern + "$", Pattern.MULTILINE); + Pattern logLineRegex = Pattern.compile("^.+" + callback + + ": ClientInfo\\[uid=\\d+,android\\.os\\.Messenger@[a-f0-9]+\\],Id=" + id + + extraPattern + "$", Pattern.MULTILINE); assertTrue("dump did not contain callback log with callback=" + callback + ", id=" + id + ", extra=" + extra + ": " + serviceDump + "\n", logLineRegex.matcher(serviceDump).find()); - } + } @Test public void construct() throws Exception { @@ -439,10 +507,10 @@ public class WifiScanningServiceTest { */ @Test public void sendSingleScanBandRequest() throws Exception { - WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, - 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, + 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings), - ScanResults.create(0, 2400, 5150, 5175)); + ScanResults.create(0, true, 2400, 5150, 5175)); } /** @@ -450,13 +518,25 @@ public class WifiScanningServiceTest { */ @Test public void sendSingleScanChannelsRequest() throws Exception { - WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, - 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400, 5150, 5175), + 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings), ScanResults.create(0, 2400, 5150, 5175)); } /** + * Do a single scan for a list of all channels and verify that it is successful. + */ + @Test + public void sendSingleScanAllChannelsRequest() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest( + channelsToSpec(2400, 2450, 5150, 5175, 5600, 5650, 5660), + 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, true, 2400, 5150, 5175)); + } + + /** * Do a single scan with no results and verify that it is successful. */ @Test @@ -561,9 +641,95 @@ public class WifiScanningServiceTest { verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource)); } - // TODO Add more single scan tests - // * disable wifi while scanning - // * disable wifi while scanning with pending scan + /** + * Send a single scan request and then disable Wi-Fi before it completes + */ + @Test + public void sendSingleScanRequestThenDisableWifi() { + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 2293; + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, requestId); + + // disable wifi + TestUtil.sendWifiScanAvailable(mBroadcastReceiver, mContext, + WifiManager.WIFI_STATE_DISABLED); + + // validate failed response + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, requestId, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + verifyNoMoreInteractions(handler); + } + + /** + * Send a single scan request, schedule a second pending scan and disable Wi-Fi before the first + * scan completes. + */ + @Test + public void sendSingleScanAndPendingScanAndListenerThenDisableWifi() { + WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 2293; + + WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 2294; + + int listenerRequestId = 2295; + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + // Request scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, requestId1); + + // Request scan 2 + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, requestId2); + + // Setup scan listener + registerScanListener(controlChannel, listenerRequestId); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, listenerRequestId); + + // disable wifi + TestUtil.sendWifiScanAvailable(mBroadcastReceiver, mContext, + WifiManager.WIFI_STATE_DISABLED); + + // validate failed response + mLooper.dispatchAll(); + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + order.verify(handler, times(2)).handleMessage(messageCaptor.capture()); + assertFailedResponse(requestId1, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted", messageCaptor.getAllValues().get(0)); + assertFailedResponse(requestId2, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted", messageCaptor.getAllValues().get(1)); + // No additional callbacks for scan listener + verifyNoMoreInteractions(handler); + } /** * Send a single scan request and then a second one after the first completes. @@ -627,8 +793,8 @@ public class WifiScanningServiceTest { } /** - * Send a single scan request and then a second one before the first completes. - * Verify that both are scheduled and succeed. + * Send a single scan request and then a second one not satisfied by the first before the first + * completes. Verify that both are scheduled and succeed. */ @Test public void sendSingleScanRequestWhilePreviousScanRunning() { @@ -693,8 +859,8 @@ public class WifiScanningServiceTest { /** - * Send a single scan request and then two more before the first completes. - * Verify that the first completes and the second two are merged. + * Send a single scan request and then two more before the first completes. Neither are + * satisfied by the first scan. Verify that the first completes and the second two are merged. */ @Test public void sendMultipleSingleScanRequestWhilePreviousScanRunning() throws RemoteException { @@ -777,32 +943,8 @@ public class WifiScanningServiceTest { mLooper.dispatchAll(); - // unfortunatally the order that these events are dispatched is dependant on the order which - // they are iterated through internally - ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - handlerOrder.verify(handler, times(4)).handleMessage(messageCaptor.capture()); - int firstListenerId = messageCaptor.getAllValues().get(0).arg2; - assertTrue(firstListenerId + " was neither " + requestId2 + " nor " + requestId3, - firstListenerId == requestId2 || firstListenerId == requestId3); - if (firstListenerId == requestId2) { - assertScanResultsMessage(requestId2, - new WifiScanner.ScanData[] {results2.getScanData()}, - messageCaptor.getAllValues().get(0)); - assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(1)); - assertScanResultsMessage(requestId3, - new WifiScanner.ScanData[] {results3.getScanData()}, - messageCaptor.getAllValues().get(2)); - assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(3)); - } else { - assertScanResultsMessage(requestId3, - new WifiScanner.ScanData[] {results3.getScanData()}, - messageCaptor.getAllValues().get(0)); - assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(1)); - assertScanResultsMessage(requestId2, - new WifiScanner.ScanData[] {results2.getScanData()}, - messageCaptor.getAllValues().get(2)); - assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(3)); - } + verifyMultipleSingleScanResults(handlerOrder, handler, requestId2, results2, requestId3, + results3); assertEquals(mWifiMetrics.getOneshotScanCount(), 3); assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 3); @@ -819,6 +961,357 @@ public class WifiScanningServiceTest { "results=" + results3.getRawScanResults().length); } + + /** + * Send a single scan request and then a second one satisfied by the first before the first + * completes. Verify that only one scan is scheduled. + */ + @Test + public void sendSingleScanRequestWhilePreviousScanRunningAndMergeIntoFirstScan() { + // Split by frequency to make it easier to determine which results each request is expecting + ScanResults results24GHz = ScanResults.create(0, 2400, 2400, 2400, 2450); + ScanResults results5GHz = ScanResults.create(0, 5150, 5150, 5175); + ScanResults resultsBoth = ScanResults.merge(results24GHz, results5GHz); + + WifiScanner.ScanSettings requestSettings1 = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 12; + ScanResults results1 = resultsBoth; + + WifiScanner.ScanSettings requestSettings2 = createRequest(WifiScanner.WIFI_BAND_24_GHZ, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 13; + ScanResults results2 = results24GHz; + + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder handlerOrder = inOrder(handler); + InOrder nativeOrder = inOrder(mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(handlerOrder, handler, requestId1); + + // Queue scan 2 (will be folded into ongoing scan) + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(resultsBoth.getScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyMultipleSingleScanResults(handlerOrder, handler, requestId1, results1, requestId2, + results2); + + assertEquals(mWifiMetrics.getOneshotScanCount(), 2); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 2); + } + + /** + * Send a single scan request and then two more before the first completes, one of which is + * satisfied by the first scan. Verify that the first two complete together the second scan is + * just for the other scan. + */ + @Test + public void sendMultipleSingleScanRequestWhilePreviousScanRunningAndMergeOneIntoFirstScan() + throws RemoteException { + // Split by frequency to make it easier to determine which results each request is expecting + ScanResults results2400 = ScanResults.create(0, 2400, 2400, 2400); + ScanResults results2450 = ScanResults.create(0, 2450); + ScanResults results1and3 = ScanResults.merge(results2400, results2450); + + WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400, 2450), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 12; + WorkSource workSource1 = new WorkSource(1121); + ScanResults results1 = results1and3; + + WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 13; + WorkSource workSource2 = new WorkSource(Binder.getCallingUid()); // don't explicitly set + ScanResults results2 = ScanResults.create(0, 2450, 5175, 2450); + + WifiScanner.ScanSettings requestSettings3 = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId3 = 15; + WorkSource workSource3 = new WorkSource(2292); + ScanResults results3 = results2400; + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder handlerOrder = inOrder(handler); + InOrder nativeOrder = inOrder(mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, workSource1); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler1 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(handlerOrder, handler, requestId1); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource1)); + + + // Queue scan 2 (will not run because previous is in progress) + // uses uid of calling process + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // Queue scan 3 (will be merged into the active scan) + sendSingleScanRequest(controlChannel, requestId3, requestSettings3, workSource3); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId3); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results1and3.getScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyMultipleSingleScanResults(handlerOrder, handler, requestId1, results1, requestId3, + results3); + // only the requests know at the beginning of the scan get blamed + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource1)); + verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource2)); + + // now that the first scan completed we expect the second and third ones to start + WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings2)); + + // dispatch scan 2 and 3 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results2.getScanData()); + eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + + verifyScanResultsRecieved(handlerOrder, handler, requestId2, results2.getScanData()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId2); + assertEquals(mWifiMetrics.getOneshotScanCount(), 3); + assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 3); + + verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource2)); + + assertDumpContainsRequestLog("addSingleScanRequest", requestId1); + assertDumpContainsRequestLog("addSingleScanRequest", requestId2); + assertDumpContainsRequestLog("addSingleScanRequest", requestId3); + assertDumpContainsCallbackLog("singleScanResults", requestId1, + "results=" + results1.getRawScanResults().length); + assertDumpContainsCallbackLog("singleScanResults", requestId2, + "results=" + results2.getRawScanResults().length); + assertDumpContainsCallbackLog("singleScanResults", requestId3, + "results=" + results3.getRawScanResults().length); + } + + /** + * Register a single scan listener and do a single scan + */ + @Test + public void registerScanListener() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = computeSingleScanNativeSettings(requestSettings); + ScanResults results = ScanResults.create(0, 2400, 5150, 5175); + + int requestId = 12; + int listenerRequestId = 13; + + startServiceAndLoadDriver(); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + registerScanListener(controlChannel, listenerRequestId); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, listenerRequestId); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results.getRawScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedRecieved(order, handler, requestId); + verifyScanResultsRecieved(order, handler, listenerRequestId, results.getScanData()); + verifyNoMoreInteractions(handler); + + assertDumpContainsRequestLog("registerScanListener", listenerRequestId); + assertDumpContainsCallbackLog("singleScanResults", listenerRequestId, + "results=" + results.getScanData().getResults().length); + } + + /** + * Register a single scan listener and do a single scan + */ + @Test + public void deregisterScanListener() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = computeSingleScanNativeSettings(requestSettings); + ScanResults results = ScanResults.create(0, 2400, 5150, 5175); + + int requestId = 12; + int listenerRequestId = 13; + + startServiceAndLoadDriver(); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + registerScanListener(controlChannel, listenerRequestId); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, listenerRequestId); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + + deregisterScanListener(controlChannel, listenerRequestId); + mLooper.dispatchAll(); + + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results.getRawScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedRecieved(order, handler, requestId); + verifyNoMoreInteractions(handler); + + assertDumpContainsRequestLog("registerScanListener", listenerRequestId); + assertDumpContainsRequestLog("deregisterScanListener", listenerRequestId); + } + + /** + * Send a single scan request and then two more before the first completes. Neither are + * satisfied by the first scan. Verify that the first completes and the second two are merged. + */ + @Test + public void scanListenerRecievesAllResults() throws RemoteException { + WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId1 = 12; + ScanResults results1 = ScanResults.create(0, 2400); + + WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId2 = 13; + ScanResults results2 = ScanResults.create(0, 2450, 5175, 2450); + + WifiScanner.ScanSettings requestSettings3 = createRequest(channelsToSpec(5150), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId3 = 15; + ScanResults results3 = ScanResults.create(0, 5150, 5150, 5150, 5150); + + WifiNative.ScanSettings nativeSettings2and3 = createSingleScanNativeSettingsForChannels( + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, channelsToSpec(2450, 5175, 5150)); + ScanResults results2and3 = ScanResults.merge(results2, results3); + + int listenerRequestId = 13; + + + startServiceAndLoadDriver(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder handlerOrder = inOrder(handler); + InOrder nativeOrder = inOrder(mWifiScannerImpl); + + // Run scan 1 + sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler1 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(handlerOrder, handler, requestId1); + + + // Queue scan 2 (will not run because previous is in progress) + sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // Queue scan 3 (will not run because previous is in progress) + sendSingleScanRequest(controlChannel, requestId3, requestSettings3, null); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId3); + + // Register scan listener + registerScanListener(controlChannel, listenerRequestId); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, listenerRequestId); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results1.getScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(handlerOrder, handler, requestId1, results1.getScanData()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1); + verifyScanResultsRecieved(handlerOrder, handler, listenerRequestId, results1.getScanData()); + + // now that the first scan completed we expect the second and third ones to start + WifiNative.ScanEventHandler eventHandler2and3 = verifyStartSingleScan(nativeOrder, + nativeSettings2and3); + + // dispatch scan 2 and 3 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results2and3.getScanData()); + eventHandler2and3.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + + verifyMultipleSingleScanResults(handlerOrder, handler, requestId2, results2, requestId3, + results3, listenerRequestId, results2and3); + + assertDumpContainsRequestLog("registerScanListener", listenerRequestId); + assertDumpContainsCallbackLog("singleScanResults", listenerRequestId, + "results=" + results1.getRawScanResults().length); + assertDumpContainsCallbackLog("singleScanResults", listenerRequestId, + "results=" + results2and3.getRawScanResults().length); + } + + private void doSuccessfulBackgroundScan(WifiScanner.ScanSettings requestSettings, WifiNative.ScanSettings nativeSettings) { startServiceAndLoadDriver(); @@ -1155,4 +1648,90 @@ public class WifiScanningServiceTest { expectSwPnoScan(order, scanSettings.second, scanResults); verifyPnoNetworkFoundRecieved(order, handler, requestId, scanResults.getRawScanResults()); } + + /** + * Tries to simulate the race scenario where a client is disconnected immediately after single + * scan request is sent to |SingleScanStateMachine|. + */ + @Test + public void processSingleScanRequestAfterDisconnect() throws Exception { + startServiceAndLoadDriver(); + BidirectionalAsyncChannel controlChannel = connectChannel(mock(Handler.class)); + mLooper.dispatchAll(); + + // Send the single scan request and then send the disconnect immediately after. + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 10; + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + // Can't call |disconnect| here because that sends |CMD_CHANNEL_DISCONNECT| followed by + // |CMD_CHANNEL_DISCONNECTED|. + controlChannel.sendMessage(Message.obtain(null, AsyncChannel.CMD_CHANNEL_DISCONNECTED, + AsyncChannel.STATUS_REMOTE_DISCONNECTION, 0, null)); + + // Now process the above 2 actions. This should result in first processing the single scan + // request (which forwards the request to SingleScanStateMachine) and then processing the + // disconnect after. + mLooper.dispatchAll(); + + // Now check that we logged the invalid request. + String serviceDump = dumpService(); + Pattern logLineRegex = Pattern.compile("^.+" + "singleScanInvalidRequest: " + + "ClientInfo\\[unknown\\],Id=" + requestId + ",bad request$", Pattern.MULTILINE); + assertTrue("dump did not contain log with ClientInfo[unknown]: " + serviceDump + "\n", + logLineRegex.matcher(serviceDump).find()); + } + + /** + * Tries to simulate the race scenario where a client is disconnected immediately after single + * scan request is sent to |SingleScanStateMachine|. + */ + @Test + public void sendScanRequestAfterUnsuccessfulSend() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 9; + + startServiceAndLoadDriver(); + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + mLooper.dispatchAll(); + + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + ScanResults results = ScanResults.create(0, 2400); + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results.getRawScanData()); + + InOrder order = inOrder(mWifiScannerImpl, handler); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler1 = verifyStartSingleScan(order, + computeSingleScanNativeSettings(requestSettings)); + verifySuccessfulResponse(order, handler, requestId); + + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedRecieved(order, handler, requestId); + verifyNoMoreInteractions(handler); + + controlChannel.sendMessage(Message.obtain(null, AsyncChannel.CMD_CHANNEL_DISCONNECTED, + AsyncChannel.STATUS_SEND_UNSUCCESSFUL, 0, null)); + mLooper.dispatchAll(); + + sendSingleScanRequest(controlChannel, requestId, requestSettings, null); + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(order, + computeSingleScanNativeSettings(requestSettings)); + verifySuccessfulResponse(order, handler, requestId); + + eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId, results.getScanData()); + verifySingleScanCompletedRecieved(order, handler, requestId); + verifyNoMoreInteractions(handler); + } } |