resolve merge conflicts of b843fca to nyc-dev
am: 2709be1ecb
Change-Id: I41ad20f61dda33ba0b3a938330f12cd273f35ae3
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index c2579e0..cb03f7a 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -49,8 +49,9 @@
}
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 @@
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 1a5a923..dc87a5b 100644
--- a/service/java/com/android/server/wifi/ScanDetail.java
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -59,6 +59,9 @@
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 e3f3192..cb44e2a 100644
--- a/service/java/com/android/server/wifi/ScanDetailCache.java
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -85,11 +85,6 @@
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 f271b4c..2dfb754 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_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;
@@ -49,31 +43,18 @@
public class SoftApManager {
private static final String TAG = "SoftApManager";
- 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;
private final String mInterfaceName;
- private String mTetherInterfaceName;
private final SoftApStateMachine mStateMachine;
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.
*/
@@ -86,40 +67,21 @@
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));
}
/**
@@ -208,104 +170,19 @@
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)) {
- 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();
@@ -350,12 +227,6 @@
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;
}
@@ -363,112 +234,5 @@
}
}
- /**
- * 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;
- }
- return HANDLED;
- }
- }
-
- 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, "Tethering reports wifi as untethered!, "
- + "shut down soft Ap");
- 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 4c930a6..c1a334a 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -398,7 +398,7 @@
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) {
@@ -713,19 +713,13 @@
}
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());
@@ -2981,11 +2975,7 @@
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);
@@ -3119,15 +3109,6 @@
}
/**
- * 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() {
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index 25ab449..b693e23 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.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 @@
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 @@
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;
@@ -1079,27 +1083,6 @@
}
/**
- * 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.
@@ -1107,10 +1090,26 @@
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 e6d285e..7f3d5d7 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -90,7 +90,8 @@
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 @@
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,8 @@
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;
// PNO settings
private int mMin5GHzRssi;
@@ -175,17 +178,15 @@
// 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 final boolean mIsFullBandScan;
- RestartSingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) {
- mIsWatchdogTriggered = isWatchdogTriggered;
+ RestartSingleScanListener(boolean isFullBandScan) {
mIsFullBandScan = isFullBandScan;
}
@Override
public void onAlarm() {
- startSingleScan(mIsWatchdogTriggered, mIsFullBandScan);
+ startSingleScan(mIsFullBandScan);
}
}
@@ -246,9 +247,6 @@
@Override
public void onSuccess() {
localLog("PeriodicScanListener onSuccess");
-
- // reset the count
- mScanRestartCount = 0;
}
@Override
@@ -277,6 +275,7 @@
public void onResults(WifiScanner.ScanData[] results) {
handleScanResults(mScanDetails, "PeriodicScanListener");
clearScanDetails();
+ mScanRestartCount = 0;
}
@Override
@@ -293,18 +292,13 @@
private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener();
- // 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.
- private class SingleScanListener implements WifiScanner.ScanListener {
+ // 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>();
- private final boolean mIsWatchdogTriggered;
- private final boolean mIsFullBandScan;
-
- SingleScanListener(boolean isWatchdogTriggered, boolean isFullBandScan) {
- mIsWatchdogTriggered = isWatchdogTriggered;
- mIsFullBandScan = isFullBandScan;
- }
public void clearScanDetails() {
mScanDetails.clear();
@@ -312,10 +306,78 @@
@Override
public void onSuccess() {
- localLog("SingleScanListener onSuccess");
+ localLog("registerScanListener onSuccess");
+ }
- // reset the count
- mSingleScanRestartCount = 0;
+ @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();
+ return;
+ }
+
+ 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, 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 final boolean mIsFullBandScan;
+
+ SingleScanListener(boolean isFullBandScan) {
+ mIsFullBandScan = isFullBandScan;
+ }
+
+ @Override
+ public void onSuccess() {
+ localLog("SingleScanListener onSuccess");
}
@Override
@@ -326,7 +388,7 @@
// 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 "
@@ -342,35 +404,10 @@
@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));
}
}
@@ -402,9 +439,6 @@
@Override
public void onSuccess() {
localLog("PnoScanListener onSuccess");
-
- // reset the count
- mScanRestartCount = 0;
}
@Override
@@ -451,6 +485,7 @@
boolean wasConnectAttempted;
wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener");
clearScanDetails();
+ mScanRestartCount = 0;
if (!wasConnectAttempted) {
// The scan results were rejected by QNS due to low RSSI values
@@ -507,6 +542,9 @@
+ " secureNetworkBonus " + mSecureBonus
+ " initialScoreMax " + mInitialScoreMax);
+ // Register for all single scan results
+ mScanner.registerScanListener(mAllSingleScanListener);
+
Log.i(TAG, "ConnectivityScanManager initialized ");
}
@@ -661,7 +699,7 @@
Log.i(TAG, "start a single scan from watchdogHandler");
scheduleWatchdogTimer();
- startSingleScan(true, true);
+ startSingleScan(true);
}
}
@@ -694,7 +732,7 @@
}
mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
- startSingleScan(false, isFullBandScan);
+ startSingleScan(isFullBandScan);
schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
// Set up the next scan interval in an exponential backoff fashion.
@@ -721,7 +759,7 @@
}
// Start a single scan
- private void startSingleScan(boolean isWatchdogTriggered, boolean isFullBandScan) {
+ private void startSingleScan(boolean isFullBandScan) {
if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
return;
}
@@ -753,7 +791,7 @@
// 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);
}
@@ -761,6 +799,12 @@
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) {
@@ -818,6 +862,7 @@
mPnoScanListener.clearScanDetails();
mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
+ mPnoScanStarted = true;
}
// Start a ConnectedPNO scan when screen is off and Wifi is connected
@@ -861,6 +906,16 @@
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
@@ -879,14 +934,23 @@
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,
@@ -943,11 +1007,11 @@
// 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;
}
@@ -970,8 +1034,10 @@
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();
}
@@ -1051,6 +1117,7 @@
if (!mWifiEnabled) {
stopConnectivityScan();
resetLastPeriodicSingleScanTimeStamp();
+ mLastConnectionAttemptBssid = null;
}
}
@@ -1065,6 +1132,7 @@
if (!mWifiConnectivityManagerEnabled) {
stopConnectivityScan();
resetLastPeriodicSingleScanTimeStamp();
+ mLastConnectionAttemptBssid = null;
}
}
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java
index 8fb3789..bfbf449 100644
--- a/service/java/com/android/server/wifi/WifiController.java
+++ b/service/java/com/android/server/wifi/WifiController.java
@@ -18,6 +18,7 @@
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 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 final boolean DBG = false;
private Context mContext;
@@ -89,9 +93,9 @@
"com.android.server.WifiManager.action.DEVICE_IDLE";
/* References to values tracked in WifiService */
- final WifiStateMachine mWifiStateMachine;
- final WifiSettingsStore mSettingsStore;
- final LockList mLocks;
+ private final WifiStateMachine mWifiStateMachine;
+ private final WifiSettingsStore mSettingsStore;
+ private final WifiLockManager mWifiLockManager;
/**
* Temporary for computing UIDS that are responsible for starting WIFI.
@@ -105,22 +109,26 @@
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();
@@ -135,14 +143,14 @@
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);
@@ -336,7 +344,7 @@
private void updateBatteryWorkSource() {
mTmpWorkSource.clear();
if (mDeviceIdle) {
- mLocks.updateWorkSource(mTmpWorkSource);
+ mTmpWorkSource.add(mWifiLockManager.createMergedWorkSource());
}
mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
}
@@ -404,6 +412,8 @@
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;
@@ -479,6 +489,9 @@
log("DEFERRED_TOGGLE handled");
sendMessage((Message)(msg.obj));
break;
+ case CMD_RESTART_WIFI_CONTINUE:
+ transitionTo(mDeviceActiveState);
+ break;
default:
return NOT_HANDLED;
}
@@ -690,7 +703,7 @@
case CMD_WIFI_TOGGLED:
if (mSettingsStore.isWifiToggleEnabled()) {
mWifiStateMachine.setHostApRunning(null, false);
- mPendingState = mStaEnabledState;
+ mPendingState = mDeviceActiveState;
}
break;
case CMD_SET_AP:
@@ -820,6 +833,10 @@
}
mFirstUserSignOnSeen = true;
return HANDLED;
+ } else if (msg.what == CMD_RESTART_WIFI) {
+ deferMessage(obtainMessage(CMD_RESTART_WIFI_CONTINUE));
+ transitionTo(mApStaDisabledState);
+ return HANDLED;
}
return NOT_HANDLED;
}
@@ -882,26 +899,23 @@
}
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 bd9b14b..1339656 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -21,6 +21,9 @@
/**
* 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,12 +152,29 @@
}
/**
- * @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() {
+ 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();
@@ -163,7 +183,7 @@
// 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
@@ -178,8 +198,8 @@
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 896c1c8..558b50e 100644
--- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
+++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
@@ -80,6 +80,8 @@
private WifiMetrics mWifiMetrics;
+ private WifiController mWifiController = null;
+
WifiLastResortWatchdog(WifiMetrics wifiMetrics) {
mWifiMetrics = wifiMetrics;
}
@@ -324,13 +326,21 @@
}
/**
- * 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 +547,14 @@
+ ", 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 0000000..fe193f0
--- /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 4251df4..c15e2a8 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 @@
/** 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 @@
private WifiStateMachine mWifiStateMachine;
private final WifiNative mWifiNative;
private final BuildProperties mBuildProperties;
- private int mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL;
+ private int mMaxRingBufferSizeBytes;
- public WifiLogger(
- WifiStateMachine wifiStateMachine, WifiNative wifiNative,
- BuildProperties buildProperties) {
+ 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;
+
mWifiStateMachine = wifiStateMachine;
mWifiNative = wifiNative;
mBuildProperties = buildProperties;
mIsLoggingEventHandlerRegistered = false;
+ mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL;
}
@Override
@@ -224,8 +231,11 @@
}
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 @@
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 ce36401..8a02764 100644
--- a/service/java/com/android/server/wifi/WifiLoggerHal.java
+++ b/service/java/com/android/server/wifi/WifiLoggerHal.java
@@ -32,8 +32,8 @@
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;
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index cc6fe00..0dc5ccf 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -42,6 +42,11 @@
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 final Object mLock = new Object();
private static final int MAX_CONNECTION_EVENTS = 256;
private Clock mClock;
@@ -53,11 +58,11 @@
* 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,16 +70,17 @@
/**
* 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();
/**
* Records the elapsedRealtime (in seconds) that represents the beginning of data
* capture for for this WifiMetricsProto
*/
+ private final SparseIntArray mRssiPollCounts = new SparseIntArray();
private long mRecordStartTimeSec;
class RouterFingerPrint {
@@ -304,10 +310,6 @@
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;
@@ -790,6 +792,20 @@
}
}
+ /**
+ * 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);
+ }
+ }
+
public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
/**
* Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
@@ -907,6 +923,13 @@
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
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());
}
}
}
@@ -919,6 +942,7 @@
*/
private void consolidateProto(boolean incremental) {
List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
+ List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
synchronized (mLock) {
for (ConnectionEvent event : mConnectionEventList) {
// If this is not incremental, dump full ConnectionEvent list
@@ -964,6 +988,18 @@
}
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);
}
}
@@ -979,6 +1015,7 @@
mScanReturnEntries.clear();
mWifiSystemStateEntries.clear();
mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
+ mRssiPollCounts.clear();
mWifiLogProto.clear();
}
}
diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java
index 2c1bde1..1f2b397 100644
--- a/service/java/com/android/server/wifi/WifiMonitor.java
+++ b/service/java/com/android/server/wifi/WifiMonitor.java
@@ -594,7 +594,7 @@
while (true) {
if (mWifiNative.connectToSupplicant()) {
mConnected = true;
- new MonitorThread().start();
+ new MonitorThread(mWifiNative.getLocalLog()).start();
return true;
}
if (connectTries++ < 5) {
@@ -723,10 +723,11 @@
}
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 f268f62..73765ee 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;
@@ -104,7 +105,7 @@
private static final LocalLog sLocalLog = new LocalLog(8192);
- public static LocalLog getLocalLog() {
+ public @NonNull LocalLog getLocalLog() {
return sLocalLog;
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java
index 380b768..edbc516 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.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;
@@ -543,12 +544,14 @@
}
}
}
- } 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 241ddb8..ee7397a 100644
--- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
@@ -98,8 +98,7 @@
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;
@@ -782,7 +781,9 @@
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);
}
@@ -796,6 +797,7 @@
currentHighestScore = highestScore;
scanResultCandidate = scanResult;
networkCandidate = configurationCandidateForThisScan;
+ networkCandidate.getNetworkSelectionStatus().setCandidate(scanResultCandidate);
}
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 7193580..740ef6e 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -130,15 +130,6 @@
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 final List<Multicaster> mMulticasters =
new ArrayList<Multicaster>();
private int mMulticastEnabled;
@@ -317,6 +308,7 @@
WifiStateMachineHandler mWifiStateMachineHandler;
private WifiController mWifiController;
+ private final WifiLockManager mWifiLockManager;
public WifiServiceImpl(Context context) {
mContext = context;
@@ -349,10 +341,13 @@
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);
+ // Set the WifiController for WifiLastResortWatchdog
+ mWifiInjector.getWifiLastResortWatchdog().setWifiController(mWifiController);
}
@@ -391,9 +386,12 @@
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);
}
}
},
@@ -1129,11 +1127,13 @@
/**
* 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;
}
/**
@@ -1510,16 +1510,9 @@
}
}
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(" ");
@@ -1532,251 +1525,35 @@
}
}
- 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);
}
+ return false;
}
- 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);
- }
+ @Override
+ public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) {
+ mWifiLockManager.updateWifiLockWorkSource(binder, ws);
}
- public boolean releaseWifiLock(IBinder lock) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- synchronized (mLocks) {
- return releaseWifiLockLocked(lock);
+ @Override
+ public boolean releaseWifiLock(IBinder binder) {
+ if (mWifiLockManager.releaseWifiLock(binder)) {
+ mWifiController.sendMessage(CMD_LOCKS_CHANGED);
+ return true;
}
+ return false;
}
- 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);
- }
-
- return hadLock;
- }
-
- 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;
@@ -1787,20 +1564,7 @@
}
}
- 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) {
@@ -1811,6 +1575,14 @@
}
}
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
public String toString() {
return "Multicaster{" + mTag + " uid=" + mUid + "}";
}
@@ -1899,6 +1671,7 @@
public void enableVerboseLogging(int verbose) {
enforceAccessPermission();
mWifiStateMachine.enableVerboseLogging(verbose);
+ mWifiLockManager.enableVerboseLogging(verbose);
}
public int getVerboseLoggingLevel() {
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 402cbe5..d89594f 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -108,6 +108,7 @@
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;
@@ -1030,7 +1031,7 @@
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();
}
@@ -1293,25 +1294,39 @@
}
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);
@@ -1869,6 +1884,14 @@
}
/**
+ * Allow tests to confirm the operational mode for WSM.
+ */
+ @VisibleForTesting
+ protected int getOperationalModeForTest() {
+ return mOperationalMode;
+ }
+
+ /**
* TODO: doc
*/
public List<ScanResult> syncGetScanResultsList() {
@@ -2143,8 +2166,8 @@
/**
* 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);
}
/**
@@ -2290,10 +2313,15 @@
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);
@@ -2888,12 +2916,16 @@
}
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);
@@ -3139,6 +3171,10 @@
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
@@ -4079,7 +4115,9 @@
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);
@@ -4277,6 +4315,7 @@
}
if (mWifiNative.startSupplicant(mP2pSupported)) {
+ setSupplicantLogLevel();
setWifiState(WIFI_STATE_ENABLING);
if (DBG) log("Supplicant start successful");
mWifiMonitor.startMonitoring(mInterfaceName);
@@ -4303,6 +4342,9 @@
transitionTo(mInitialState);
}
break;
+ case CMD_SET_OPERATIONAL_MODE:
+ mOperationalMode = message.arg1;
+ break;
default:
return NOT_HANDLED;
}
@@ -4497,7 +4539,7 @@
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:
@@ -4794,7 +4836,9 @@
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);
}
@@ -5598,24 +5642,11 @@
&& 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) {
@@ -5657,12 +5688,6 @@
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();
@@ -6674,10 +6699,11 @@
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);
}
@@ -7343,6 +7369,9 @@
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");
@@ -7443,7 +7472,19 @@
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;
@@ -7658,7 +7699,7 @@
checkAndSetConnectivityInstance();
mSoftApManager = mFacade.makeSoftApManager(
mContext, getHandler().getLooper(), mWifiNative, mNwService,
- mCm, mCountryCode.getCurrentCountryCode(),
+ mCm, mCountryCode.getCountryCode(),
mWifiApConfigStore.getAllowed2GChannel(),
new SoftApListener());
mSoftApManager.start(config);
@@ -7869,6 +7910,7 @@
return result;
}
+ // TODO move to TelephonyUtil, same with utilities above
String getGsmSimAuthResponse(String[] requestData, TelephonyManager tm) {
StringBuilder sb = new StringBuilder();
for (String challenge : requestData) {
@@ -7930,6 +7972,7 @@
return sb.toString();
}
+ // TODO move to TelephonyUtil
void handleGsmAuthRequest(SimAuthRequestData requestData) {
if (targetWificonfiguration == null
|| targetWificonfiguration.networkId == requestData.networkId) {
@@ -7957,6 +8000,7 @@
}
}
+ // TODO move to TelephonyUtil
void handle3GAuthRequest(SimAuthRequestData requestData) {
StringBuilder sb = new StringBuilder();
byte[] rand = null;
diff --git a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java
index 070c69b..9bcfddb 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 @@
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 @@
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 @@
}
*/
- 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 @@
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 @@
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/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index ae75a37..c1d9445 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -1359,8 +1359,20 @@
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/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index d279482..6ae2237 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 @@
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 @@
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 @@
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: {
@@ -164,9 +165,7 @@
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);
- }
+ localLog("client disconnected: " + client + ", reason: " + msg.arg1);
client.cleanup();
}
return;
@@ -215,6 +214,15 @@
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 +251,8 @@
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 +429,7 @@
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 +558,24 @@
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 +621,7 @@
@Override
public void exit() {
+ mActiveScanSettings = null;
try {
mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
} catch (RemoteException e) {
@@ -639,7 +663,7 @@
}
}
- 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 +675,46 @@
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 +773,8 @@
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 +809,10 @@
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 +823,26 @@
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 +1052,11 @@
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 +1196,13 @@
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 +1293,22 @@
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 +1907,7 @@
}
public void cleanup() {
+ mSingleScanListeners.removeAllForClient(this);
mSingleScanStateMachine.removeSingleScanRequests(this);
mBackgroundScanStateMachine.removeBackgroundScanSettings(this);
mBackgroundScanStateMachine.removeHotlistSettings(this);
@@ -1891,7 +1979,7 @@
@Override
public String toString() {
- return "ClientInfo[uid=" + mUid + "]";
+ return "ClientInfo[uid=" + mUid + "," + mMessenger + "]";
}
}
@@ -1988,6 +2076,11 @@
public void sendRequestToClientHandler(int what) {
sendRequestToClientHandler(what, null, null);
}
+
+ @Override
+ public String toString() {
+ return "InternalClientInfo[]";
+ }
}
void replySucceeded(Message msg) {
@@ -2080,9 +2173,12 @@
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 +2210,11 @@
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 +2266,11 @@
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 +2496,11 @@
}
}
- 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 +2508,14 @@
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 +2629,7 @@
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) {
@@ -2541,7 +2650,7 @@
StringBuilder sb = new StringBuilder();
sb.append(callback)
.append(": ")
- .append(ci.toString())
+ .append((ci == null) ? "ClientInfo[unknown]" : ci.toString())
.append(",Id=")
.append(id);
if (extra != null) {
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 0000000..3d7ce45
--- /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/proto/wifi.proto b/service/proto/wifi.proto
index 43a4166..3b0e854 100644
--- a/service/proto/wifi.proto
+++ b/service/proto/wifi.proto
@@ -186,6 +186,9 @@
// 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;
}
// Information that gets logged for every WiFi connection.
@@ -337,3 +340,12 @@
// 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;
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index c091517..b91df5a 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.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.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 @@
@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 @@
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 @@
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 @@
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 145c840..3993fe5 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 @@
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 @@
+ "}\n";
@Mock private WifiNative mWifiNative;
+ @Mock private Context mContext;
private MockKeyStore mMockKeyStore;
private WifiConfigStore mWifiConfigStore;
@@ -163,8 +165,8 @@
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 2434c2d..022997d 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.*;
@@ -45,6 +46,7 @@
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;
@@ -128,6 +130,10 @@
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
@@ -144,6 +150,7 @@
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 +222,7 @@
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 +329,30 @@
}
/**
+ * 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()
@@ -854,4 +886,62 @@
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);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
index 44a710a..c187faf 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 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 @@
@Mock FrameworkFacade mFacade;
@Mock WifiSettingsStore mSettingsStore;
@Mock WifiStateMachine mWifiStateMachine;
- @Mock WifiServiceImpl.LockList mLockList;
+ @Mock WifiLockManager mWifiLockManager;
WifiController mWifiController;
@@ -94,7 +99,7 @@
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 @@
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 2e62a30..faa2f71 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 @@
// Wifi get L2 connected.
mWifiCountryCode.setReadyForChange(false);
verify(mWifiNative).setCountryCode(anyString());
- assertEquals(mDefaultCountryCode, mWifiCountryCode.getCurrentCountryCode());
+ assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
}
/**
@@ -81,13 +81,13 @@
@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 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 @@
// 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 @@
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 @@
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 @@
// 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 64f1ce0..237fc66 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 @@
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 org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
import java.util.ArrayList;
import java.util.Arrays;
@@ -38,7 +40,8 @@
@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 @@
@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 @@
ssids[ssids.length - 1], bssids[ssids.length - 1],
WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
assertEquals(true, watchdogTriggered);
+ verify(mWifiController).sendMessage(WifiController.CMD_RESTART_WIFI);
+ reset(mWifiController);
}
/**
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 0000000..1bbdda9
--- /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 55dc683..d915ff3 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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 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 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 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 @@
// 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 @@
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 530b218..011682b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -154,6 +154,8 @@
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_RSSI_LEVELS_TO_INCREMENT = 20;
+ private static final int FIRST_RSSI_LEVEL = -80;
/**
* Set simple metrics, increment others
*/
@@ -236,6 +238,11 @@
for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER; i++) {
mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadOther();
}
+ for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
+ for (int j = 0; j <= i; j++) {
+ mWifiMetrics.incrementRssiPollRssiCount(FIRST_RSSI_LEVEL + i);
+ }
+ }
}
/**
@@ -313,6 +320,10 @@
mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadOther);
assertEquals(TEST_RECORD_DURATION_SEC,
mDeserializedWifiMetrics.recordDurationSec);
+ for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
+ assertEquals(FIRST_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi);
+ assertEquals(i + 1, mDeserializedWifiMetrics.rssiPollRssiCount[i].count);
+ }
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
index 64fee84..92de7d4 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
@@ -2233,4 +2233,41 @@
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);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index e0f94ad..53d8f46 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -475,6 +475,75 @@
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/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
index 62f5105..03a11dc 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -39,6 +39,7 @@
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 @@
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 @@
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 @@
((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 @@
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 @@
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 @@
}
}
- 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,8 +365,9 @@
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());
@@ -309,8 +376,9 @@
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());
@@ -561,9 +629,95 @@
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 +781,8 @@
}
/**
- * 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 +847,8 @@
/**
- * 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 +931,8 @@
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 +949,357 @@
"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 +1636,39 @@
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, 0,
+ 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());
+ }
+
}