diff options
author | Mitchell Wills <mwills@google.com> | 2015-11-03 17:16:09 -0800 |
---|---|---|
committer | Mitchell Wills <mwills@google.com> | 2016-01-29 09:53:34 -0800 |
commit | 772124d1f1ddb2b9537de5efc748943808dafe80 (patch) | |
tree | 1355a426c9910d9de1958d3dd3eaa14d564aaee3 | |
parent | 8541f6411048c5c6281699153b8c7c73e1f30c4c (diff) |
Refactor WifiScanner support for oneshot scans
Seperate oneshot scans so they are no longer implemented using
background scans and make WifiStateMachine use WifiScanner for single
scans.
This changes requires a few parts:
1. Implement single scan logic in WifiScanningServiceImpl for merging
and executing scan requests using the single scan native interface.
2. Writing tests for the new code in 1
3. Make supplicant start scans by using WifiScanner and when it
recieves callbacks dispatch scan complete/failure messages.
4. Enable HalWifiScannerImpl and SupplicantWifiScannerImpl to listen to
supplicant scan events now that WifiStateMachine does not.
Bug: 26525037
Change-Id: Iddd6d64b35fed129048e1fd5c79acb6982bfc418
15 files changed, 1224 insertions, 327 deletions
diff --git a/service/java/com/android/server/wifi/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/HalWifiScannerImpl.java index fc5d18f4a..dc6fc1b61 100644 --- a/service/java/com/android/server/wifi/HalWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/HalWifiScannerImpl.java @@ -50,14 +50,10 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb mWifiNative = wifiNative; mEventHandler = new Handler(looper, this); - // We can't enable these until WifiStateMachine switches to using WifiScanner because - // WifiMonitor only supports sending results to one listener - // TODO Enable these - // Also need to fix tests again when this is enabled - // WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - // WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); - // WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - // WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); + WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), + WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); + WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), + WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); } @Override @@ -66,7 +62,9 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb case WifiMonitor.SCAN_FAILED_EVENT: Log.w(TAG, "Single scan failed"); if (mSingleScanEventHandler != null) { - // TODO indicate failure to caller + if (mSingleScanEventHandler != null) { + mSingleScanEventHandler.onScanStatus(WifiNative.WIFI_SCAN_DISABLED); + } mSingleScanEventHandler = null; } break; @@ -116,9 +114,17 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb } mSingleScanEventHandler = eventHandler; - if (!mWifiNative.scan(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, freqs)) { - mSingleScanEventHandler = null; - // TODO call on failure callback in handler + if (!mWifiNative.scan(freqs)) { + Log.e(TAG, "Failed to start scan, freqs=" + freqs); + // indicate scan failure async + mEventHandler.post(new Runnable() { + public void run() { + if (mSingleScanEventHandler != null) { + mSingleScanEventHandler.onScanStatus(WifiNative.WIFI_SCAN_DISABLED); + } + mSingleScanEventHandler = null; + } + }); } return true; } @@ -148,7 +154,7 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb Arrays.sort(results, SCAN_RESULT_SORT_COMPARATOR); mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, results); if (mSingleScanEventHandler != null) { - mSingleScanEventHandler.onScanResultsAvailable(); + mSingleScanEventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); mSingleScanEventHandler = null; } } diff --git a/service/java/com/android/server/wifi/ScanDetailUtil.java b/service/java/com/android/server/wifi/ScanDetailUtil.java new file mode 100644 index 000000000..07eaafbb6 --- /dev/null +++ b/service/java/com/android/server/wifi/ScanDetailUtil.java @@ -0,0 +1,41 @@ +/* + * 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.net.wifi.ScanResult; + +import com.android.server.wifi.hotspot2.NetworkDetail; + +/** + * Utility for converting a ScanResult to a ScanDetail. + * Only fields that are suppported in ScanResult are copied. + */ +public class ScanDetailUtil { + private ScanDetailUtil() { /* not constructable */ } + + public static ScanDetail toScanDetail(ScanResult scanResult) { + NetworkDetail networkDetail = new NetworkDetail(scanResult.BSSID, + scanResult.informationElements, scanResult.anqpLines, scanResult.frequency); + ScanDetail scanDetail = new ScanDetail(networkDetail, scanResult.wifiSsid, + scanResult.BSSID, scanResult.capabilities, scanResult.level, scanResult.frequency, + scanResult.timestamp); + scanDetail.getScanResult().informationElements = scanResult.informationElements; + scanDetail.getScanResult().anqpLines = scanResult.anqpLines; + return scanDetail; + } + +} diff --git a/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java index 78e213797..8c8b3f27d 100644 --- a/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java @@ -95,14 +95,10 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mEventHandler = new Handler(looper, this); - // We can't enable these until WifiStateMachine switches to using WifiScanner because - // WifiMonitor only supports sending results to one listener - // TODO Enable these - // Also need to fix tests again when this is enabled - // WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - // WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); - // WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - // WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); + WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), + WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); + WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), + WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); } @Override @@ -115,6 +111,7 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle capabilities.max_hotlist_bssids = 0; capabilities.max_significant_wifi_change_aps = 0; // TODO reenable once scan results handlers are enabled again + // NOTE: this only disables background scan requests return false; } @@ -255,7 +252,8 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle } Set<Integer> freqs = new HashSet<>(); - LastScanSettings newScanSettings = new LastScanSettings(SystemClock.elapsedRealtime()); + final LastScanSettings newScanSettings = + new LastScanSettings(SystemClock.elapsedRealtime()); // Update scan settings if there is a pending scan if (!mBackgroundScanPaused) { @@ -358,8 +356,7 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle } if (freqs.size() > 0) { - boolean success = mWifiNative.scan( - WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, freqs); + boolean success = mWifiNative.scan(freqs); if (success) { // TODO handle scan timeout Log.d(TAG, "Starting wifi scan for " + freqs.size() + " freqs" @@ -367,8 +364,18 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle + ", single=" + newScanSettings.singleScanActive); mLastScanSettings = newScanSettings; } else { - Log.w(TAG, "Failed starting wifi scan for " + freqs.size() + " freqs"); - // TODO indicate failure for single and background scans + Log.e(TAG, "Failed to start scan, freqs=" + freqs); + // indicate scan failure async + mEventHandler.post(new Runnable() { + public void run() { + newScanSettings.singleScanActive = false; + if (newScanSettings.singleScanEventHandler != null) { + newScanSettings.singleScanEventHandler + .onScanStatus(WifiNative.WIFI_SCAN_DISABLED); + } + } + }); + // TODO if scans fail enough background scans should be failed as well } } } @@ -457,7 +464,8 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle / 100) || mBackgroundScanBuffer.size() >= mLastScanSettings.reportNumScansThreshold))) { - mBackgroundScanEventHandler.onScanStatus(); + mBackgroundScanEventHandler + .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); } } @@ -484,7 +492,8 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, singleScanResults.toArray(new ScanResult[singleScanResults.size()])); - mLastScanSettings.singleScanEventHandler.onScanResultsAvailable(); + mLastScanSettings.singleScanEventHandler + .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); } mLastScanSettings = null; diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 8166328dc..0560345f6 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -324,12 +324,9 @@ public class WifiNative { } - public static final int SCAN_WITHOUT_CONNECTION_SETUP = 1; - public static final int SCAN_WITH_CONNECTION_SETUP = 2; - - public boolean scan(int type, Set<Integer> freqs) { + public boolean scan(Set<Integer> freqs) { if(freqs == null) { - return scan(type, (String)null); + return scan((String) null); } else if (freqs.size() != 0) { StringBuilder freqList = new StringBuilder(); @@ -340,22 +337,18 @@ public class WifiNative { freqList.append(freq.toString()); first = false; } - return scan(type, freqList.toString()); + return scan(freqList.toString()); } else { return false; } } - private boolean scan(int type, String freqList) { - if (type == SCAN_WITHOUT_CONNECTION_SETUP) { - if (freqList == null) return doBooleanCommand("SCAN TYPE=ONLY"); - else return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList); - } else if (type == SCAN_WITH_CONNECTION_SETUP) { - if (freqList == null) return doBooleanCommand("SCAN"); - else return doBooleanCommand("SCAN freq=" + freqList); + private boolean scan(String freqList) { + if (freqList == null) { + return doBooleanCommand("SCAN TYPE=ONLY"); // Scan all channels } else { - throw new IllegalArgumentException("Invalid scan type"); + return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList); } } @@ -677,6 +670,7 @@ public class WifiNative { ScanDetail scan = new ScanDetail(networkDetail, wifiSsid, bssid, flags, level, freq, tsf); scan.getScanResult().informationElements = infoElements; + scan.getScanResult().anqpLines = anqpLines; results.add(scan); } catch (IllegalArgumentException iae) { Log.d(TAG, "Failed to parse information elements: " + iae); @@ -1731,18 +1725,17 @@ public class WifiNative { } public static interface ScanEventHandler { - void onScanResultsAvailable(); void onFullScanResult(ScanResult fullScanResult); - void onScanStatus(); + void onScanStatus(int event); void onScanPaused(WifiScanner.ScanData[] data); void onScanRestarted(); } /* scan status, keep these values in sync with gscan.h */ - private static final int WIFI_SCAN_RESULTS_AVAILABLE = 0; - private static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1; - private static final int WIFI_SCAN_THRESHOLD_PERCENT = 2; - private static final int WIFI_SCAN_DISABLED = 3; + public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0; + public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1; + public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2; + public static final int WIFI_SCAN_DISABLED = 3; // Callback from native private static void onScanStatus(int id, int event) { @@ -1751,7 +1744,7 @@ public class WifiNative { || event == WIFI_SCAN_THRESHOLD_PERCENT) { if (handler != null) { // TODO pass event back to framework - handler.onScanStatus(); + handler.onScanStatus(event); } } else if (event == WIFI_SCAN_DISABLED) { diff --git a/service/java/com/android/server/wifi/WifiScanningScheduler.java b/service/java/com/android/server/wifi/WifiScanningScheduler.java index 7b36b9918..744afd55c 100644 --- a/service/java/com/android/server/wifi/WifiScanningScheduler.java +++ b/service/java/com/android/server/wifi/WifiScanningScheduler.java @@ -28,17 +28,17 @@ import java.util.Collection; */ public abstract class WifiScanningScheduler { - private static final int DEFAULT_MAX_BUCKETS = 8; - private static final int DEFAULT_MAX_CHANNELS = 32; + static final int DEFAULT_MAX_BUCKETS = 8; + static final int DEFAULT_MAX_CHANNELS = 32; // anecdotally, some chipsets will fail without explanation with a higher batch size, and // there is apparently no way to retrieve the maximum batch size - private static final int DEFAULT_MAX_SCANS_TO_BATCH = 10; - private static final int DEFAULT_MAX_AP_PER_SCAN = 32; + static final int DEFAULT_MAX_SCANS_TO_BATCH = 10; + static final int DEFAULT_MAX_AP_PER_SCAN = 32; - private static int mMaxBuckets = DEFAULT_MAX_BUCKETS; - private static int mMaxChannels = DEFAULT_MAX_CHANNELS; - private static int mMaxBatch = DEFAULT_MAX_SCANS_TO_BATCH; - private static int mMaxApPerScan = DEFAULT_MAX_AP_PER_SCAN; + private int mMaxBuckets = DEFAULT_MAX_BUCKETS; + private int mMaxChannels = DEFAULT_MAX_CHANNELS; + private int mMaxBatch = DEFAULT_MAX_SCANS_TO_BATCH; + private int mMaxApPerScan = DEFAULT_MAX_AP_PER_SCAN; int getMaxBuckets() { return mMaxBuckets; diff --git a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java index 777b3f9b6..3d61c1a63 100644 --- a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java @@ -43,6 +43,8 @@ import android.os.WorkSource; import android.util.ArrayMap; import android.util.LocalLog; import android.util.Log; +import android.util.Pair; +import android.util.SparseIntArray; import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; @@ -190,13 +192,15 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE: case WifiScanner.CMD_START_TRACKING_CHANGE: case WifiScanner.CMD_STOP_TRACKING_CHANGE: + mStateMachine.sendMessage(Message.obtain(msg)); + break; case WifiScanner.CMD_START_SINGLE_SCAN: case WifiScanner.CMD_STOP_SINGLE_SCAN: - mStateMachine.sendMessage(Message.obtain(msg)); - return; + mSingleScanStateMachine.sendMessage(Message.obtain(msg)); + break; default: replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request"); - return; + break; } } } @@ -213,7 +217,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final int CMD_DRIVER_UNLOADED = BASE + 7; private static final int CMD_SCAN_PAUSED = BASE + 8; private static final int CMD_SCAN_RESTARTED = BASE + 9; - private static final int CMD_STOP_SCAN_INTERNAL = BASE + 10; + private static final int CMD_SCAN_DISABLED = BASE + 10; private final Context mContext; private final Looper mLooper; @@ -224,6 +228,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private WifiNative.ScanSettings mPreviousSchedule; private WifiScanningStateMachine mStateMachine; + private WifiSingleScanStateMachine mSingleScanStateMachine; private ClientHandler mClientHandler; private final IBatteryStats mBatteryStats; @@ -243,6 +248,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mClientHandler = new ClientHandler(mLooper); mStateMachine = new WifiScanningStateMachine(mLooper); mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper); + mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper); mContext.registerReceiver( new BroadcastReceiver() { @@ -253,16 +259,342 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { if (DBG) localLog("SCAN_AVAILABLE : " + state); if (state == WifiManager.WIFI_STATE_ENABLED) { mStateMachine.sendMessage(CMD_DRIVER_LOADED); + mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED); } else if (state == WifiManager.WIFI_STATE_DISABLED) { mStateMachine.sendMessage(CMD_DRIVER_UNLOADED); + mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); } } }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE)); mStateMachine.start(); mWifiChangeStateMachine.start(); + mSingleScanStateMachine.start(); + } + + /** + * A map of objects of a WifiScanner client and handler id to an object of type T + */ + private static class ClientHandlerMap<T> extends HashMap<Pair<ClientInfo, Integer>, T> { + public ClientHandlerMap(int initialSize) { + super(initialSize); + } + + public T put(ClientInfo ci, int handler, T value) { + return put(Pair.create(ci, handler), value); + } + + public T get(ClientInfo ci, int handler) { + return get(Pair.create(ci, handler)); + } + + public T remove(ClientInfo ci, int handler) { + return remove(Pair.create(ci, handler)); + } } + /** + * State machine that holds the state of single scans. Scans should only be active in the + * ScanningState. The pending scans and active scans maps are swaped when entering + * ScanningState. Any requests queued while scanning will be placed in the pending queue and + * executed after transitioning back to NotScanningState. + */ + class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler { + private final DefaultState mDefaultState = new DefaultState(); + private final DriverStartedState mDriverStartedState = new DriverStartedState(); + private final NotScanningState mNotScanningState = new NotScanningState(); + private final ScanningState mScanningState = new ScanningState(); + + private ClientHandlerMap<ScanSettings> mActiveScans = new ClientHandlerMap<>(4); + private ClientHandlerMap<ScanSettings> mPendingScans = new ClientHandlerMap<>(4); + + public WifiSingleScanStateMachine(Looper looper) { + super("WifiSingleScanStateMachine", looper); + + setLogRecSize(128); + setLogOnlyTransitions(false); + + // CHECKSTYLE:OFF IndentationCheck + addState(mDefaultState); + addState(mDriverStartedState, mDefaultState); + addState(mNotScanningState, mDriverStartedState); + addState(mScanningState, mDriverStartedState); + // CHECKSTYLE:ON IndentationCheck + + setInitialState(mDefaultState); + } + + /** + * Called to indicate a change in state for the current scan. + * Will dispatch a coresponding event to the state machine + */ + @Override + public void onScanStatus(int event) { + if (DBG) localLog("onScanStatus event received, event=" + event); + switch(event) { + case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: + case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: + case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: + sendMessage(CMD_SCAN_RESULTS_AVAILABLE); + break; + case WifiNative.WIFI_SCAN_DISABLED: + sendMessage(CMD_SCAN_DISABLED); + break; + default: + Log.e(TAG, "Unknown scan status event: " + event); + break; + } + } + + /** + * Called for each full scan result if requested + */ + @Override + public void onFullScanResult(ScanResult fullScanResult) { + if (DBG) localLog("onFullScanResult received"); + sendMessage(CMD_FULL_SCAN_RESULTS, 0, 0, fullScanResult); + } + + @Override + public void onScanPaused(ScanData scanData[]) { + // should not happen for single scan + Log.e(TAG, "Got scan paused for single scan"); + } + + @Override + public void onScanRestarted() { + // should not happen for single scan + Log.e(TAG, "Got scan restarted for single scan"); + } + + class DefaultState extends State { + @Override + public void enter() { + mActiveScans.clear(); + mPendingScans.clear(); + } + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_DRIVER_LOADED: + transitionTo(mNotScanningState); + return HANDLED; + case CMD_DRIVER_UNLOADED: + transitionTo(mDefaultState); + return HANDLED; + case WifiScanner.CMD_START_SINGLE_SCAN: + case WifiScanner.CMD_STOP_SINGLE_SCAN: + replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available"); + return HANDLED; + case CMD_SCAN_RESULTS_AVAILABLE: + if (DBG) localLog("ignored scan results available event"); + return HANDLED; + case CMD_FULL_SCAN_RESULTS: + if (DBG) localLog("ignored full scan result event"); + return HANDLED; + default: + return NOT_HANDLED; + } + + } + } + + /** + * State representing when the driver is running. This state is not meant to be transitioned + * directly, but is instead indented as a parent state of ScanningState and NotScanningState + * to hold common functionality and handle cleaning up scans when the driver is shut down. + */ + class DriverStartedState extends State { + @Override + public void exit() { + sendOpFailedToAll(mPendingScans, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + mPendingScans.clear(); + } + + @Override + public boolean processMessage(Message msg) { + ClientInfo ci = mClients.get(msg.replyTo); + + switch (msg.what) { + case WifiScanner.CMD_START_SINGLE_SCAN: + if (validateAndAddToScanQueue(ci, msg.arg2, (ScanSettings) msg.obj)) { + replySucceeded(msg); + // if were not currently scanning then try to start + if (getCurrentState() != mScanningState) { + tryToStartNewScan(); + } + } else { + replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); + } + return HANDLED; + case WifiScanner.CMD_STOP_SINGLE_SCAN: + removeSingleScanRequest(ci, msg.arg2); + return HANDLED; + default: + return NOT_HANDLED; + } + } + } + + class NotScanningState extends State { + @Override + public void enter() { + tryToStartNewScan(); + } + + @Override + public boolean processMessage(Message msg) { + return NOT_HANDLED; + } + } + + class ScanningState extends State { + @Override + public void exit() { + // if any scans are still active (never got results available then indicate failure + sendOpFailedToAll(mActiveScans, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + mActiveScans.clear(); + } + + // TODO correctly handle battery blaming + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_SCAN_RESULTS_AVAILABLE: + reportScanResults(mScannerImpl.getLatestSingleScanResults()); + mActiveScans.clear(); + transitionTo(mNotScanningState); + return HANDLED; + case CMD_FULL_SCAN_RESULTS: + reportFullScanResult((ScanResult) msg.obj); + return HANDLED; + case CMD_SCAN_DISABLED: + sendOpFailedToAll(mActiveScans, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + mActiveScans.clear(); + transitionTo(mNotScanningState); + return HANDLED; + default: + return NOT_HANDLED; + } + } + } + + boolean validateAndAddToScanQueue(ClientInfo ci, int handler, ScanSettings settings) { + if (ci == null) { + Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); + return false; + } + ChannelSpec[] channels = WifiChannelHelper.getChannelsForScanSettings(settings); + if (channels.length == 0) { + Log.d(TAG, "Failing single scan because channel list was empty"); + return false; + } + logScanRequest("validateSingleScanAndAddToScanQueue", ci, handler, settings); + mPendingScans.put(ci, handler, settings); + return true; + } + + void removeSingleScanRequest(ClientInfo ci, int handler) { + if (ci != null) { + logScanRequest("removeSingleScanRequest", ci, handler, null); + mPendingScans.remove(ci, handler); + mActiveScans.remove(ci, handler); + } + } + + void tryToStartNewScan() { + // TODO move merging logic to a scheduler + int reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + ArrayList<Integer> allChannels = new ArrayList<>(); + for (ScanSettings settings : mPendingScans.values()) { + ChannelSpec[] channels = WifiChannelHelper.getChannelsForScanSettings(settings); + for (int i = 0; i < channels.length; ++i) { + allChannels.add(channels[i].frequency); + } + if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { + reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; + } + } + if (allChannels.size() > 0) { + WifiNative.ScanSettings settings = new WifiNative.ScanSettings(); + settings.num_buckets = 1; + WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); + bucketSettings.bucket = 0; + bucketSettings.period_ms = 0; + bucketSettings.report_events = reportEvents; + bucketSettings.num_channels = allChannels.size(); + bucketSettings.channels = new WifiNative.ChannelSettings[allChannels.size()]; + for (int i = 0; i < bucketSettings.channels.length; ++i) { + bucketSettings.channels[i] = new WifiNative.ChannelSettings(); + bucketSettings.channels[i].frequency = allChannels.get(i); + } + settings.buckets = new WifiNative.BucketSettings[] {bucketSettings}; + if (mScannerImpl.startSingleScan(settings, this)) { + // swap pending and active scan requests + ClientHandlerMap<ScanSettings> tmp = mActiveScans; + mActiveScans = mPendingScans; + mPendingScans = tmp; + // make sure that the pending list is clear + mPendingScans.clear(); + transitionTo(mScanningState); + } else { + // notify and cancel failed scans + sendOpFailedToAll(mPendingScans, WifiScanner.REASON_UNSPECIFIED, + "Failed to start single scan"); + mPendingScans.clear(); + } + } else { + // notify and cancel failed scans + sendOpFailedToAll(mPendingScans, WifiScanner.REASON_INVALID_REQUEST, + "No channels specified"); + mPendingScans.clear(); + } + } + + void sendOpFailedToAll(ClientHandlerMap<?> clientHandlers, int reason, String description) { + for (Pair<ClientInfo, Integer> key : clientHandlers.keySet()) { + ClientInfo ci = key.first; + int handler = key.second; + ci.sendMessage(WifiScanner.CMD_OP_FAILED, 0, handler, + new WifiScanner.OperationResult(reason, description)); + } + } + + // TODO fix shouldReport checks + // currently these checks work becuase the shouldReport methods don't yet rely on the + // internal state of the scheduler. + void reportFullScanResult(ScanResult result) { + for (Map.Entry<Pair<ClientInfo, Integer>, ScanSettings> entry + : mActiveScans.entrySet()) { + ClientInfo ci = entry.getKey().first; + int handler = entry.getKey().second; + ScanSettings settings = entry.getValue(); + if (mScheduler.shouldReportFullScanResultForSettings(result, settings)) { + ci.sendMessage(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, result); + } + } + } + + void reportScanResults(ScanData results) { + for (Map.Entry<Pair<ClientInfo, Integer>, ScanSettings> entry + : mActiveScans.entrySet()) { + ClientInfo ci = entry.getKey().first; + int handler = entry.getKey().second; + ScanSettings settings = entry.getValue(); + ScanData[] resultsArray = new ScanData[] {results}; + ScanData[] resultsToDeliver = + mScheduler.filterResultsForSettings(resultsArray, settings); + WifiScanner.ParcelableScanData parcelableScanData = + new WifiScanner.ParcelableScanData(resultsToDeliver); + ci.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData); + } + } + } + + class WifiScanningStateMachine extends StateMachine implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler, WifiNative.SignificantWifiChangeEventHandler { @@ -276,23 +608,31 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { setLogRecSize(512); setLogOnlyTransitions(false); + // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); addState(mStartedState, mDefaultState); addState(mPausedState, mDefaultState); + // CHECKSTYLE:ON IndentationCheck setInitialState(mDefaultState); } @Override - public void onScanResultsAvailable() { - if (DBG) localLog("onScanResultAvailable event received"); - sendMessage(CMD_SCAN_RESULTS_AVAILABLE); - } - - @Override - public void onScanStatus() { - if (DBG) localLog("onScanStatus event received"); - sendMessage(CMD_SCAN_RESULTS_AVAILABLE); + public void onScanStatus(int event) { + if (DBG) localLog("onScanStatus event received, event=" + event); + switch(event) { + case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE: + case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS: + case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT: + sendMessage(CMD_SCAN_RESULTS_AVAILABLE); + break; + case WifiNative.WIFI_SCAN_DISABLED: + sendMessage(CMD_SCAN_DISABLED); + break; + default: + Log.e(TAG, "Unknown scan status event: " + event); + break; + } } @Override @@ -340,6 +680,9 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { public boolean processMessage(Message msg) { switch (msg.what) { case CMD_DRIVER_LOADED: + // TODO this should be moved to a common location since it is used outside + // of this state machine. It is ok right now becuase the driver loaded event + // is sent to this state machine first. if (mScannerImpl == null) { mScannerImpl = mScannerImplFactory.create(mContext, mLooper); } @@ -408,33 +751,19 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { case CMD_DRIVER_UNLOADED: return NOT_HANDLED; case WifiScanner.CMD_START_BACKGROUND_SCAN: - if (addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) { + if (addBackgroundScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) { replySucceeded(msg); } else { replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); } break; case WifiScanner.CMD_STOP_BACKGROUND_SCAN: - removeScanRequest(ci, msg.arg2); + removeBackgroundScanRequest(ci, msg.arg2); break; case WifiScanner.CMD_GET_SCAN_RESULTS: reportScanResults(mScannerImpl.getLatestBatchedScanResults(true)); replySucceeded(msg); break; - case WifiScanner.CMD_START_SINGLE_SCAN: - if (addSingleScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) { - replySucceeded(msg); - } else { - replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request"); - } - break; - case WifiScanner.CMD_STOP_SINGLE_SCAN: - removeScanRequest(ci, msg.arg2); - break; - case CMD_STOP_SCAN_INTERNAL: - localLog("Removing single shot scan"); - removeScanRequest((ClientInfo) msg.obj, msg.arg2); - break; case WifiScanner.CMD_SET_HOTLIST: setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj); replySucceeded(msg); @@ -491,6 +820,10 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { reportScanResults((ScanData[]) msg.obj); transitionTo(mPausedState); break; + case CMD_SCAN_DISABLED: + // TODO handle this gracefully (currently no implementations are know to + // report this) + break; default: return NOT_HANDLED; } @@ -523,8 +856,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private class ClientInfo { private final AsyncChannel mChannel; + /** + * Indicates if the client is still connected + * If the client is no longer connected then messages to it will be signlently dropped + */ + private boolean mDisconnected = false; private final int mUid; private final WorkSource mWorkSource; + private boolean mScanWorkReported = false; ClientInfo(int uid, AsyncChannel c) { @@ -534,6 +873,12 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { if (DBG) localLog("New client, channel: " + c); } + void sendMessage(int what, int arg1, int arg2, Object obj) { + if (!mDisconnected) { + mChannel.sendMessage(what, arg1, arg2, obj); + } + } + void reportBatchedScanStart() { if (mUid == 0) return; @@ -560,7 +905,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { int getCsph() { int csph = 0; - for (ScanSettings settings : getScanSettings()) { + for (ScanSettings settings : getBackgroundScanSettings()) { int num_channels = WifiChannelHelper.getChannelsForScanSettings(settings).length; int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs; @@ -575,7 +920,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { reportBatchedScanStop(); mScanWorkReported = false; } - if (mScanSettings.isEmpty() == false) { + if (mBackgroundScanSettings.isEmpty()) { reportBatchedScanStart(); mScanWorkReported = true; } @@ -583,14 +928,15 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public String toString() { - return "ClientInfo[uid=" + mUid + ", channel=" + mChannel + "]"; + return "ClientInfo[uid=" + mUid + "]"; } void dump(FileDescriptor fd, PrintWriter pw, String[] args) { StringBuilder sb = new StringBuilder(); sb.append(toString()); - Iterator<Map.Entry<Integer, ScanSettings>> it = mScanSettings.entrySet().iterator(); + Iterator<Map.Entry<Integer, ScanSettings>> it = + mBackgroundScanSettings.entrySet().iterator(); for (; it.hasNext(); ) { Map.Entry<Integer, ScanSettings> entry = it.next(); sb.append("ScanId ").append(entry.getKey()).append("\n"); @@ -603,33 +949,26 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { pw.println(sb.toString()); } - HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4); - HashMap<Integer, Integer> mScanPeriods = new HashMap<Integer, Integer>(4); + private final Map<Integer, ScanSettings> mBackgroundScanSettings = + new HashMap<Integer, ScanSettings>(4); + private final SparseIntArray mBackgroundScanPeriods = new SparseIntArray(4); - void addScanRequest(ScanSettings settings, int id) { - mScanSettings.put(id, settings); + void addBackgroundScanRequest(ScanSettings settings, int id) { + mBackgroundScanSettings.put(id, settings); reportScanWorkUpdate(); } - void removeScanRequest(int id) { - ScanSettings settings = mScanSettings.remove(id); - if (settings != null && settings.periodInMs == 0) { - /* this was a single shot scan */ - mChannel.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, id); - } + void removeBackgroundScanRequest(int id) { + mBackgroundScanSettings.remove(id); reportScanWorkUpdate(); } - Iterator<Map.Entry<Integer, ScanSettings>> getScans() { - return mScanSettings.entrySet().iterator(); - } - - Collection<ScanSettings> getScanSettings() { - return mScanSettings.values(); + Collection<ScanSettings> getBackgroundScanSettings() { + return mBackgroundScanSettings.values(); } void reportScanResults(ScanData[] results) { - Iterator<Integer> it = mScanSettings.keySet().iterator(); + Iterator<Integer> it = mBackgroundScanSettings.keySet().iterator(); while (it.hasNext()) { int handler = it.next(); reportScanResults(results, handler); @@ -637,7 +976,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } void reportScanResults(ScanData[] results, int handler) { - ScanSettings settings = mScanSettings.get(handler); + ScanSettings settings = mBackgroundScanSettings.get(handler); ScanData[] resultsToDeliver = mScheduler.filterResultsForSettings(results, settings); @@ -646,11 +985,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { deliverScanResults(handler, resultsToDeliver); } - - if (settings.periodInMs == 0) { - /* this is a single shot scan; stop the scan now */ - mStateMachine.sendMessage(CMD_STOP_SCAN_INTERNAL, 0, handler, this); - } } void deliverScanResults(int handler, ScanData results[]) { @@ -660,10 +994,10 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } void reportFullScanResult(ScanResult result) { - Iterator<Integer> it = mScanSettings.keySet().iterator(); + Iterator<Integer> it = mBackgroundScanSettings.keySet().iterator(); while (it.hasNext()) { int handler = it.next(); - ScanSettings settings = mScanSettings.get(handler); + ScanSettings settings = mBackgroundScanSettings.get(handler); if (mScheduler.shouldReportFullScanResultForSettings(result, settings)) { ScanResult newResult = new ScanResult(result); if (result.informationElements != null) { @@ -678,18 +1012,18 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) { - Integer prevPeriodObject = mScanPeriods.get(handler); - int prevPeriodInMs = settings.periodInMs; - if (prevPeriodObject != null) { - prevPeriodInMs = prevPeriodObject; + int prevPeriodInMs = mBackgroundScanPeriods.get(handler, 0); + if (prevPeriodInMs == 0) { + prevPeriodInMs = settings.periodInMs; } if (prevPeriodInMs != newPeriodInMs) { + mBackgroundScanPeriods.put(handler, newPeriodInMs); mChannel.sendMessage(WifiScanner.CMD_PERIOD_CHANGED, newPeriodInMs, handler); } } - private final HashMap<Integer, WifiScanner.HotlistSettings> mHotlistSettings = + private final Map<Integer, WifiScanner.HotlistSettings> mHotlistSettings = new HashMap<Integer, WifiScanner.HotlistSettings>(); void addHostlistSettings(WifiScanner.HotlistSettings settings, int handler) { @@ -780,7 +1114,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } void cleanup() { - mScanSettings.clear(); + mDisconnected = true; + mBackgroundScanSettings.clear(); updateSchedule(); mHotlistSettings.clear(); @@ -829,7 +1164,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private boolean updateSchedule() { ArrayList<ScanSettings> settings = new ArrayList<>(); for (ClientInfo client : mClients.values()) { - settings.addAll(client.getScanSettings()); + settings.addAll(client.getBackgroundScanSettings()); } mScheduler.updateSchedule(settings); @@ -897,7 +1232,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { localLog(sb.toString()); } - boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) { + boolean addBackgroundScanRequest(ClientInfo ci, int handler, ScanSettings settings) { // sanity check the input if (ci == null) { Log.d(TAG, "Failing scan request ClientInfo not found " + handler); @@ -941,48 +1276,22 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - logScanRequest("addScanRequest", ci, handler, settings); - ci.addScanRequest(settings, handler); - - if (updateSchedule()) { - return true; - } else { - ci.removeScanRequest(handler); - localLog("Failing scan request because failed to reset scan"); - return false; - } - } - - boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) { - if (ci == null) { - Log.d(TAG, "Failing single scan request ClientInfo not found " + handler); - return false; - } - if (settings.reportEvents == 0) { - settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; - } - if (settings.periodInMs == 0) { - settings.periodInMs = 10000; // 10s - although second scan should never happen - } - - logScanRequest("addSingleScanRequest", ci, handler, settings); - ci.addScanRequest(settings, handler); + logScanRequest("addBackgroundScanRequest", ci, handler, settings); + ci.addBackgroundScanRequest(settings, handler); if (updateSchedule()) { - /* reset periodInMs to 0 to indicate single shot scan */ - settings.periodInMs = 0; return true; } else { - ci.removeScanRequest(handler); + ci.removeBackgroundScanRequest(handler); localLog("Failing scan request because failed to reset scan"); return false; } } - void removeScanRequest(ClientInfo ci, int handler) { + void removeBackgroundScanRequest(ClientInfo ci, int handler) { if (ci != null) { - logScanRequest("removeScanRequest", ci, handler, null); - ci.removeScanRequest(handler); + logScanRequest("removeBackgroundScanRequest", ci, handler, null); + ci.removeBackgroundScanRequest(handler); updateSchedule(); } } @@ -1125,9 +1434,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mClients.put(null, mClientInfo); + // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); - addState(mStationaryState, mDefaultState); - addState(mMovingState, mDefaultState); + addState(mStationaryState, mDefaultState); + addState(mMovingState, mDefaultState); + // CHECKSTYLE:ON IndentationCheck setInitialState(mDefaultState); } @@ -1531,7 +1842,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } pw.println("listeners:"); for (ClientInfo client : mClients.values()) { - for (ScanSettings settings : client.getScanSettings()) { + for (ScanSettings settings : client.getBackgroundScanSettings()) { pw.println(" " + toString(client.mUid, settings)); } } diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index ec4d815d6..1a4c2ece0 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -1400,8 +1400,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.RX_HS20_ANQP_ICON_EVENT, getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SCAN_FAILED_EVENT, getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SCAN_RESULTS_EVENT, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SSID_REENABLED, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SSID_TEMP_DISABLED, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_CONNECTION_EVENT, getHandler()); @@ -1997,7 +1995,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno } } - private void handleScanRequest(int type, Message message) { + private void handleScanRequest(Message message) { ScanSettings settings = null; WorkSource workSource = null; @@ -2018,7 +2016,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno } // call wifi native to start the scan - if (startScanNative(type, freqs)) { + if (startScanNative(freqs)) { // only count battery consumption if scan request is accepted noteScanStart(message.arg1, workSource); // a full scan covers everything, clearing scan request buffer @@ -2069,17 +2067,57 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno } + // TODO this is a temporary measure to bridge between WifiScanner and WifiStateMachine until + // scan functionality is refactored out of WifiStateMachine. + private WifiScanner.ScanListener mLastNativeScanListener = null; + private volatile ArrayList<ScanDetail> mLastNativeScanResults = new ArrayList<ScanDetail>(); /** * return true iff scan request is accepted */ - private boolean startScanNative(int type, Set<Integer> freqs) { - if (mWifiNative.scan(type, freqs)) { - mIsScanOngoing = true; - mIsFullScanOngoing = (freqs == null); - lastScanFreqs = freqs; - return true; + private boolean startScanNative(Set<Integer> freqs) { + if (mLastNativeScanListener != null) { + mWifiScanner.stopScan(mLastNativeScanListener); + mLastNativeScanListener = null; + } + WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings(); + if (freqs == null) { + settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS; + } else { + settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED; + int index = 0; + settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; + for (Integer freq : freqs) { + settings.channels[index++] = new WifiScanner.ChannelSpec(freq); + } } - return false; + settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; + mLastNativeScanListener = new WifiScanner.ScanListener() { + private final ArrayList<ScanDetail> mNewScanResults = new ArrayList<>(); + + public void onSuccess() { + // the scan started successfully, but has not completed yet + } + public void onFailure(int reason, String description) { + Log.e(TAG, "Scan failed: reason=" + reason + ", " + description); + sendMessage(WifiMonitor.SCAN_FAILED_EVENT); + } + public void onResults(WifiScanner.ScanData[] results) { + mLastNativeScanResults = mNewScanResults; + sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); + } + public void onFullResult(ScanResult fullScanResult) { + mNewScanResults.add(ScanDetailUtil.toScanDetail(fullScanResult)); + } + public void onPeriodChanged(int periodInMs) { + // does not occur for single scan + } + }; + mWifiScanner.startScan(settings, mLastNativeScanListener); + mIsScanOngoing = true; + mIsFullScanOngoing = (freqs == null); + lastScanFreqs = freqs; + return true; } /** @@ -3943,7 +3981,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno mNumScanResultsKnown = 0; mNumScanResultsReturned = 0; - ArrayList<ScanDetail> scanResults = mWifiNative.getScanResults(); + ArrayList<ScanDetail> scanResults = mLastNativeScanResults; if (scanResults.isEmpty()) { mScanResults = new ArrayList<>(); @@ -5013,13 +5051,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno mDhcpActive = false; } - void connectScanningService() { - - if (mWifiScanner == null) { - mWifiScanner = (WifiScanner) mContext.getSystemService(Context.WIFI_SCANNING_SERVICE); - } - } - private void handleIPv4Success(DhcpResults dhcpResults, int reason) { if (PDBG) { @@ -6155,11 +6186,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno class DriverStartedState extends State { @Override public void enter() { - if (PDBG) { logd("DriverStartedState enter"); } + // We can't do this in the constructor because WifiStateMachine is created before the + // wifi scanning service is initialized + if (mWifiScanner == null) { + mWifiScanner = + (WifiScanner) mContext.getSystemService(Context.WIFI_SCANNING_SERVICE); + } + mWifiLogger.startLogging(mVerboseLoggingLevel > 0); mIsRunning = true; updateBatteryWorkSource(null); @@ -6252,7 +6289,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno switch(message.what) { case CMD_START_SCAN: - handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); + handleScanRequest(message); break; case CMD_SET_FREQUENCY_BAND: int band = message.arg1; @@ -6265,7 +6302,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno // Flush old data - like scan results mWifiNative.bssFlush(); // Fetch the latest scan results when frequency band is set -// startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); + // startScanNative(null); if (PDBG) logd("done set frequency band " + band); @@ -6541,7 +6578,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno // Handle scan. All the connection related commands are // handled only in ConnectModeState case CMD_START_SCAN: - handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); + handleScanRequest(message); break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); @@ -7039,11 +7076,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno class ConnectModeState extends State { @Override - public void enter() { - connectScanningService(); - } - - @Override public boolean processMessage(Message message) { WifiConfiguration config; int netId; @@ -8089,8 +8121,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno logd("starting scan for " + config.configKey() + " with " + freqs); //} // Call wifi native to start the scan - if (startScanNative( - WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, freqs)) { + if (startScanNative(freqs)) { // Only count battery consumption if scan request is accepted noteScanStart(SCAN_ALARM_SOURCE, null); messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK; @@ -8383,8 +8414,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno + fullBandConnectedTimeIntervalMilli); } } - handleScanRequest( - WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); + handleScanRequest(message); } else { if (!startScanForConfiguration( currentConfiguration, restrictChannelList)) { @@ -8405,8 +8435,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno + fullBandConnectedTimeIntervalMilli); } } - handleScanRequest( - WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); + handleScanRequest(message); } } @@ -9357,7 +9386,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno } /* Disable background scan temporarily during a regular scan */ enableBackgroundScan(false); - handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); + handleScanRequest(message); ret = HANDLED; } else { diff --git a/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java b/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java index 0ff439152..9a21843f4 100644 --- a/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java @@ -25,7 +25,6 @@ import static com.android.server.wifi.ScanTestUtil.setupMockChannels; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -140,7 +139,7 @@ public abstract class BaseWifiScannerImplTest { WifiNative.ScanEventHandler eventHandler2 = mock(WifiNative.ScanEventHandler.class); // scan start succeeds - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); assertTrue(mScanner.startSingleScan(settings, eventHandler)); assertFalse("second scan while first scan running should fail immediately", @@ -164,12 +163,13 @@ public abstract class BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // scan fails - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(false); + when(mWifiNative.scan(any(Set.class))).thenReturn(false); // start scan assertTrue(mScanner.startSingleScan(settings, eventHandler)); - // TODO expect failure callback once implemented + mLooper.dispatchAll(); + order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_DISABLED); verifyNoMoreInteractions(eventHandler); } @@ -213,7 +213,7 @@ public abstract class BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // scans succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // start first scan assertTrue(mScanner.startSingleScan(settings, eventHandler)); @@ -257,7 +257,7 @@ public abstract class BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // scan succeeds - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // start scan assertTrue(mScanner.startSingleScan(settings, eventHandler)); @@ -270,8 +270,7 @@ public abstract class BaseWifiScannerImplTest { protected void expectSuccessfulSingleScan(InOrder order, WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScan, ScanResults results, boolean expectFullResults) { - order.verify(mWifiNative).scan(eq(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP), - eq(expectedScan)); + order.verify(mWifiNative).scan(eq(expectedScan)); when(mWifiNative.getScanResults()).thenReturn(results.getScanDetailArrayList()); @@ -286,7 +285,7 @@ public abstract class BaseWifiScannerImplTest { } } - order.verify(eventHandler).onScanResultsAvailable(); + order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); assertScanDataEquals(results.getScanData(), mScanner.getLatestSingleScanResults()); } } diff --git a/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java index 162fc2c0c..d253ff723 100644 --- a/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java @@ -16,13 +16,10 @@ package com.android.server.wifi; -import android.os.Handler; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; -import java.lang.reflect.Field; - /** * Unit tests for {@link com.android.server.wifi.HalWifiScannerImpl}. */ @@ -32,14 +29,5 @@ public class HalWifiScannerTest extends BaseWifiScannerImplTest { @Before public void setUp() throws Exception { mScanner = new HalWifiScannerImpl(WifiNative.getWlanNativeInterface(), mLooper.getLooper()); - - // TODO remove this once HalWifiScannerImpl wifi monitor registration is enabled - Field eventHandlerField = HalWifiScannerImpl.class.getDeclaredField("mEventHandler"); - eventHandlerField.setAccessible(true); - Handler eventHandler = (Handler) eventHandlerField.get(mScanner); - WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - WifiMonitor.SCAN_FAILED_EVENT, eventHandler); - WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - WifiMonitor.SCAN_RESULTS_EVENT, eventHandler); } } diff --git a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java b/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java index 97da4a473..94ce31d38 100644 --- a/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/MultiClientSchedulerTest.java @@ -16,6 +16,8 @@ package com.android.server.wifi; +import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; +import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals; import static com.android.server.wifi.ScanTestUtil.channelsToSpec; import static com.android.server.wifi.ScanTestUtil.createRequest; import static com.android.server.wifi.ScanTestUtil.getAllChannels; @@ -44,7 +46,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Set; /** * Unit tests for {@link com.android.server.wifi.MultiClientScheduler}. @@ -459,43 +460,23 @@ public class MultiClientSchedulerTest { mScheduler.updateSchedule(requests); WifiNative.ScanSettings schedule = mScheduler.getSchedule(); - assertEquals("base_period_ms", computeExpectedPeriod(settings.periodInMs), - schedule.base_period_ms); - assertBuckets(schedule, 1); + int expectedPeriod = computeExpectedPeriod(settings.periodInMs); + NativeScanSettingsBuilder expectedBuilder = new NativeScanSettingsBuilder() + .withBasePeriod(expectedPeriod) + .withMaxApPerScan(settings.numBssidsPerScan == 0 + ? DEFAULT_MAX_AP_PER_SCAN + : settings.numBssidsPerScan) + .withMaxScansToCache(settings.maxScansToCache == 0 + ? DEFAULT_MAX_BATCH + : settings.maxScansToCache); - if (settings.numBssidsPerScan == 0) { - assertEquals("bssids per scan", DEFAULT_MAX_AP_PER_SCAN, schedule.max_ap_per_scan); - } else { - assertEquals("bssids per scan", settings.numBssidsPerScan, schedule.max_ap_per_scan); - } - if (settings.maxScansToCache == 0) { - assertEquals("scans to cache", DEFAULT_MAX_BATCH, - schedule.report_threshold_num_scans); - } else { - assertEquals("scans to cache", settings.maxScansToCache, - schedule.report_threshold_num_scans); - } - assertEquals("reportEvents", settings.reportEvents, schedule.buckets[0].report_events); - assertEquals("period", computeExpectedPeriod(settings.periodInMs), - schedule.buckets[0].period_ms); if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { - assertEquals("band", settings.band, schedule.buckets[0].band); - Set<Integer> expectedChannels = new HashSet<>(); - for (ChannelSpec channel : getAllChannels(settings)) { - expectedChannels.add(channel.frequency); - } - Set<Integer> actualChannels = new HashSet<>(); - for (ChannelSpec channel : getAllChannels(schedule.buckets[0])) { - actualChannels.add(channel.frequency); - } - assertEquals("channels", expectedChannels, actualChannels); - } - else { - assertEquals("band", settings.band, schedule.buckets[0].band); - assertEquals("num_channels", 0, schedule.buckets[0].num_channels); - assertTrue("channels", schedule.buckets[0].channels == null - || schedule.buckets[0].channels.length == 0); + expectedBuilder.addBucketWithChannels(expectedPeriod, settings.reportEvents, + getAllChannels(settings)); + } else { + expectedBuilder.addBucketWithBand(expectedPeriod, settings.reportEvents, settings.band); } + assertNativeScanSettingsEquals(expectedBuilder.build(), schedule); } private void assertBuckets(WifiNative.ScanSettings schedule, int numBuckets) { diff --git a/tests/wifitests/src/com/android/server/wifi/ScanResults.java b/tests/wifitests/src/com/android/server/wifi/ScanResults.java index d418451fc..b6007b26c 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanResults.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanResults.java @@ -28,7 +28,9 @@ import java.math.BigInteger; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Random; /** @@ -39,6 +41,29 @@ public class ScanResults { private final ScanData mScanData; private final ScanResult[] mScanResults; + private ScanResults(ArrayList<ScanDetail> scanDetails, ScanData scanData, + ScanResult[] scanResults) { + mScanDetails.addAll(scanDetails); + mScanData = scanData; + mScanResults = scanResults; + } + + public static ScanResults merge(ScanResults... others) { + ArrayList<ScanDetail> scanDetails = new ArrayList<>(); + ArrayList<ScanResult> scanDataResults = new ArrayList<>(); + ArrayList<ScanResult> rawScanResults = new ArrayList<>(); + for (ScanResults other : others) { + scanDetails.addAll(other.getScanDetailArrayList()); + scanDataResults.addAll(Arrays.asList(other.getScanData().getResults())); + rawScanResults.addAll(Arrays.asList(other.getRawScanResults())); + } + Collections.sort(scanDataResults, SCAN_RESULT_RSSI_COMPARATOR); + int id = others[0].getScanData().getId(); + return new ScanResults(scanDetails, new ScanData(id, 0, scanDataResults + .toArray(new ScanResult[scanDataResults.size()])), + rawScanResults.toArray(new ScanResult[rawScanResults.size()])); + } + private static String generateBssid(Random r) { return String.format("%02X:%02X:%02X:%02X:%02X:%02X", r.nextInt(256), r.nextInt(256), r.nextInt(256), @@ -102,11 +127,14 @@ public class ScanResults { int rssi = r.nextInt(40) - 99; // -99 to -60 ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1]; ie[0] = generateSsidIe(ssid); - NetworkDetail nd = new NetworkDetail(bssid, ie, new ArrayList<String>(), freq); + List<String> anqpLines = new ArrayList<>(); + NetworkDetail nd = new NetworkDetail(bssid, ie, anqpLines, freq); ScanDetail detail = new ScanDetail(nd, WifiSsid.createFromAsciiEncoded(ssid), bssid, "", rssi, freq, Long.MAX_VALUE); /* needed so that scan results aren't rejected because there older than scan start */ + detail.getScanResult().informationElements = ie; + detail.getScanResult().anqpLines = anqpLines; results[i] = detail; } return results; diff --git a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java index 3b06c0386..cc583e420 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java @@ -18,6 +18,7 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; import android.net.wifi.ScanResult; @@ -27,8 +28,6 @@ import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiScanner.ScanSettings; import android.net.wifi.WifiSsid; -import com.android.server.wifi.WifiNative.BucketSettings; - import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashSet; @@ -115,6 +114,10 @@ public class ScanTestUtil { mSettings.report_threshold_num_scans = maxScans; return this; } + public NativeScanSettingsBuilder withMaxPercentToCache(int percent) { + mSettings.report_threshold_percent = percent; + return this; + } public NativeScanSettingsBuilder addBucketWithBand( int period, int reportEvents, int band) { @@ -128,6 +131,11 @@ public class ScanTestUtil { public NativeScanSettingsBuilder addBucketWithChannels( int period, int reportEvents, int... channels) { + return addBucketWithChannels(period, reportEvents, channelsToSpec(channels)); + } + + public NativeScanSettingsBuilder addBucketWithChannels( + int period, int reportEvents, ChannelSpec... channels) { WifiNative.BucketSettings bucket = new WifiNative.BucketSettings(); bucket.bucket = mSettings.num_buckets; bucket.band = WifiScanner.WIFI_BAND_UNSPECIFIED; @@ -135,7 +143,7 @@ public class ScanTestUtil { bucket.channels = new WifiNative.ChannelSettings[channels.length]; for (int i = 0; i < channels.length; ++i) { bucket.channels[i] = new WifiNative.ChannelSettings(); - bucket.channels[i].frequency = channels[i]; + bucket.channels[i].frequency = channels[i].frequency; } bucket.period_ms = period; bucket.report_events = reportEvents; @@ -154,6 +162,26 @@ public class ScanTestUtil { } } + public static WifiNative.ScanSettings computeSingleScanNativeSettings( + WifiScanner.ScanSettings requestSettings) { + return createSingleScanNativeSettings(requestSettings.reportEvents, + getAllChannels(requestSettings)); + } + + public static WifiNative.ScanSettings createSingleScanNativeSettings( + int reportEvents, ChannelSpec... channels) { + // TODO requests using bands should be scheduled using the band, but are currently + // converted to channels before scheduling. This should probably change + return new NativeScanSettingsBuilder() + .withBasePeriod(0) + .withMaxApPerScan(0) + .withMaxPercentToCache(0) + .withMaxScansToCache(0) + .addBucketWithChannels(0, + reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, channels) + .build(); + } + public static Set<Integer> createFreqSet(int... elements) { Set<Integer> set = new HashSet<>(); for (int e : elements) { @@ -237,7 +265,46 @@ public class ScanTestUtil { return channelSpecs; } - public static ChannelSpec[] getAllChannels(BucketSettings bucket) { + public static void assertNativeScanSettingsEquals(WifiNative.ScanSettings expected, + WifiNative.ScanSettings actual) { + assertEquals("bssids per scan", expected.max_ap_per_scan, actual.max_ap_per_scan); + assertEquals("scans to cache", expected.report_threshold_num_scans, + actual.report_threshold_num_scans); + assertEquals("percent to cache", expected.report_threshold_percent, + actual.report_threshold_percent); + assertEquals("base period", expected.base_period_ms, actual.base_period_ms); + + assertEquals("number of buckets", expected.num_buckets, actual.num_buckets); + assertNotNull("buckets was null", actual.buckets); + for (int i = 0; i < expected.buckets.length; ++i) { + assertNotNull("buckets[" + i + "] was null", actual.buckets[i]); + assertEquals("buckets[" + i + "].period", + expected.buckets[i].period_ms, actual.buckets[i].period_ms); + assertEquals("buckets[" + i + "].reportEvents", + expected.buckets[i].report_events, actual.buckets[i].report_events); + + assertEquals("buckets[" + i + "].band", + expected.buckets[i].band, actual.buckets[i].band); + if (expected.buckets[i].band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + Set<Integer> expectedChannels = new HashSet<>(); + for (ChannelSpec channel : getAllChannels(expected.buckets[i])) { + expectedChannels.add(channel.frequency); + } + Set<Integer> actualChannels = new HashSet<>(); + for (ChannelSpec channel : getAllChannels(actual.buckets[i])) { + actualChannels.add(channel.frequency); + } + assertEquals("channels", expectedChannels, actualChannels); + } else { + assertEquals("buckets[" + i + "].num_channels", 0, actual.buckets[i].num_channels); + assertTrue("buckets[" + i + "].channels", + actual.buckets[i].channels == null + || actual.buckets[i].channels.length == 0); + } + } + } + + public static ChannelSpec[] getAllChannels(WifiNative.BucketSettings bucket) { if (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { ChannelSpec[] channels = new ChannelSpec[bucket.num_channels]; for (int i = 0; i < bucket.num_channels; i++) { diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java index b08e52dc8..94b9dc330 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java @@ -22,7 +22,6 @@ import static com.android.server.wifi.ScanTestUtil.createFreqSet; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -32,14 +31,12 @@ import static org.mockito.Mockito.when; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; -import android.os.Handler; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Set; @@ -53,15 +50,6 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { public void setup() throws Exception { mScanner = new SupplicantWifiScannerImpl(mContext, WifiNative.getWlanNativeInterface(), mLooper.getLooper()); - - // TODO remove this once SupplicantWifiScannerImpl wifi monitor registration is enabled - Field eventHandlerField = SupplicantWifiScannerImpl.class.getDeclaredField("mEventHandler"); - eventHandlerField.setAccessible(true); - Handler eventHandler = (Handler) eventHandlerField.get(mScanner); - WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - WifiMonitor.SCAN_FAILED_EVENT, eventHandler); - WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(), - WifiMonitor.SCAN_RESULTS_EVENT, eventHandler); } @Test @@ -332,7 +320,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scans fail - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(false); + when(mWifiNative.scan(any(Set.class))).thenReturn(false); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -367,7 +355,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scan starts succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -412,7 +400,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scan starts succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -467,7 +455,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scan starts succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -477,8 +465,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { // alarm for next period assertEquals(1, mAlarmManager.getPendingCount()); - order.verify(mWifiNative).scan(eq(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP), - eq(expectedPeriods[0].getScanFreqs())); + order.verify(mWifiNative).scan(eq(expectedPeriods[0].getScanFreqs())); mScanner.pauseBatchedScan(); @@ -544,7 +531,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scan starts succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -583,7 +570,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { InOrder order = inOrder(eventHandler, mWifiNative); // All scans succeed - when(mWifiNative.scan(anyInt(), any(Set.class))).thenReturn(true); + when(mWifiNative.scan(any(Set.class))).thenReturn(true); // Start scan mScanner.startBatchedScan(settings, eventHandler); @@ -643,7 +630,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { ArrayList<ScanDetail> nativeResults, WifiScanner.ScanData[] expectedScanResults, ScanResult[] fullResults, int periodId) { // Verify scan started - order.verify(mWifiNative).scan(eq(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP), eq(scanFreqs)); + order.verify(mWifiNative).scan(eq(scanFreqs)); // Setup scan results when(mWifiNative.getScanResults()).thenReturn(nativeResults); @@ -660,7 +647,7 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { if (expectedScanResults != null) { // Verify scan results delivered - order.verify(eventHandler).onScanStatus(); + order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); assertScanDatasEquals("period[" + periodId + "].", expectedScanResults, mScanner.getLatestBatchedScanResults(true)); } @@ -669,22 +656,26 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { private void expectFailedScanStart(InOrder order, WifiNative.ScanEventHandler eventHandler, Set<Integer> scanFreqs) { // Verify scan started - order.verify(mWifiNative).scan(eq(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP), eq(scanFreqs)); + order.verify(mWifiNative).scan(eq(scanFreqs)); + + // TODO verify failure event } private void expectFailedEventScan(InOrder order, WifiNative.ScanEventHandler eventHandler, Set<Integer> scanFreqs) { // Verify scan started - order.verify(mWifiNative).scan(eq(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP), eq(scanFreqs)); + order.verify(mWifiNative).scan(eq(scanFreqs)); // Notify scan has failed mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT); assertEquals("dispatch message after results event", 1, mLooper.dispatchAll()); + + // TODO verify failure event } private void dispatchOnlyAlarm() { assertEquals("dispatch only one alarm", 1, mAlarmManager.dispatchAll()); - assertEquals("dispatch only one message", 1, mLooper.dispatchAll()); + mLooper.dispatchAll(); } private static class ScanPeriod { diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java index 4093936b6..42d89525a 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java @@ -16,12 +16,21 @@ package com.android.server.wifi; +import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder; +import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals; +import static com.android.server.wifi.ScanTestUtil.assertScanDatasEquals; +import static com.android.server.wifi.ScanTestUtil.channelsToSpec; +import static com.android.server.wifi.ScanTestUtil.computeSingleScanNativeSettings; import static com.android.server.wifi.ScanTestUtil.createRequest; +import static com.android.server.wifi.ScanTestUtil.createSingleScanNativeSettings; import static com.android.server.wifi.ScanTestUtil.installWlanWifiNative; import static com.android.server.wifi.ScanTestUtil.setupMockChannels; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; @@ -48,6 +57,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -89,13 +99,16 @@ public class WifiScanningServiceTest { } - ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); + /** + * Internal BroadcastReceiver that WifiScanningServiceImpl uses to listen for broadcasts + * this is initialized by calling startServiceAndLoadDriver + */ + BroadcastReceiver mBroadcastReceiver; private void sendWifiScanAvailable(int scanAvailable) { Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, scanAvailable); - mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + mBroadcastReceiver.onReceive(mContext, intent); } private WifiScanner.ScanSettings generateValidScanSettings() { @@ -112,45 +125,96 @@ public class WifiScanningServiceTest { return controlChannel; } - private Message sendRequest(BidirectionalAsyncChannel controlChannel, Handler handler, - Message msg) { - controlChannel.sendMessage(msg); - mLooper.dispatchAll(); + private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler) { ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(handler).handleMessage(messageCaptor.capture()); + order.verify(handler).handleMessage(messageCaptor.capture()); return messageCaptor.getValue(); } - private void sendAndAssertSuccessfulyBackgroundScan(BidirectionalAsyncChannel controlChannel, - Handler handler, int scanRequestId, WifiScanner.ScanSettings settings) { - Message response = sendRequest(controlChannel, handler, - Message.obtain(null, WifiScanner.CMD_START_BACKGROUND_SCAN, 0, scanRequestId, - settings)); - assertEquals("response.what", WifiScanner.CMD_OP_SUCCEEDED, response.what); - assertEquals("response.arg2", scanRequestId, response.arg2); - assertEquals("response.obj", null, response.obj); + private void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, + WifiScanner.ScanData... expected) { + Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler); + assertScanResultsMessage(listenerId, expected, scanResultMessage); + } + + private void assertScanResultsMessage(int listenerId, WifiScanner.ScanData[] expected, + Message scanResultMessage) { + assertEquals("what", WifiScanner.CMD_SCAN_RESULT, scanResultMessage.what); + assertEquals("listenerId", listenerId, scanResultMessage.arg2); + assertScanDatasEquals(expected, + ((WifiScanner.ParcelableScanData) scanResultMessage.obj).getResults()); + } + + private void sendBackgroundScanRequest(BidirectionalAsyncChannel controlChannel, + int scanRequestId, WifiScanner.ScanSettings settings) { + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_START_BACKGROUND_SCAN, 0, + scanRequestId, settings)); + } + + private void sendSingleScanRequest(BidirectionalAsyncChannel controlChannel, + int scanRequestId, WifiScanner.ScanSettings settings) { + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_START_SINGLE_SCAN, 0, + scanRequestId, settings)); + } + + private void verifySuccessfulResponse(InOrder order, Handler handler, int arg2) { + Message response = verifyHandleMessageAndGetMessage(order, handler); + assertSuccessfulResponse(arg2, response); + } + + private 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 + + ", description=" + result.description); + } else { + assertEquals("response.what", WifiScanner.CMD_OP_SUCCEEDED, response.what); + assertEquals("response.arg2", arg2, response.arg2); + } } - private void sendAndAssertFailedBackgroundScan(BidirectionalAsyncChannel controlChannel, - Handler handler, int scanRequestId, WifiScanner.ScanSettings settings, + private void verifyFailedResponse(InOrder order, Handler handler, int arg2, int expectedErrorReason, String expectedErrorDescription) { - Message response = sendRequest(controlChannel, handler, - Message.obtain(null, WifiScanner.CMD_START_BACKGROUND_SCAN, 0, scanRequestId, - settings)); - assertFailedResponse(scanRequestId, expectedErrorReason, expectedErrorDescription, - response); + Message response = verifyHandleMessageAndGetMessage(order, handler); + assertFailedResponse(arg2, expectedErrorReason, expectedErrorDescription, response); } private void assertFailedResponse(int arg2, int expectedErrorReason, String expectedErrorDescription, Message response) { - assertEquals("response.what", WifiScanner.CMD_OP_FAILED, response.what); - assertEquals("response.arg2", arg2, response.arg2); - assertEquals("response.obj.reason", - expectedErrorReason, ((WifiScanner.OperationResult) response.obj).reason); - assertEquals("response.obj.description", - expectedErrorDescription, ((WifiScanner.OperationResult) response.obj).description); + if (response.what == WifiScanner.CMD_OP_SUCCEEDED) { + fail("response indicates success"); + } else { + assertEquals("response.what", WifiScanner.CMD_OP_FAILED, response.what); + assertEquals("response.arg2", arg2, response.arg2); + WifiScanner.OperationResult result = (WifiScanner.OperationResult) response.obj; + assertEquals("response.obj.reason", + expectedErrorReason, result.reason); + assertEquals("response.obj.description", + expectedErrorDescription, result.description); + } } + private WifiNative.ScanEventHandler verifyStartSingleScan(InOrder order, + WifiNative.ScanSettings expected) { + ArgumentCaptor<WifiNative.ScanSettings> scanSettingsCaptor = + ArgumentCaptor.forClass(WifiNative.ScanSettings.class); + ArgumentCaptor<WifiNative.ScanEventHandler> scanEventHandlerCaptor = + ArgumentCaptor.forClass(WifiNative.ScanEventHandler.class); + order.verify(mWifiScannerImpl).startSingleScan(scanSettingsCaptor.capture(), + scanEventHandlerCaptor.capture()); + assertNativeScanSettingsEquals(expected, scanSettingsCaptor.getValue()); + return scanEventHandlerCaptor.getValue(); + } + + private void verifyStartBackgroundScan(InOrder order, WifiNative.ScanSettings expected) { + ArgumentCaptor<WifiNative.ScanSettings> scanSettingsCaptor = + ArgumentCaptor.forClass(WifiNative.ScanSettings.class); + order.verify(mWifiScannerImpl).startBatchedScan(scanSettingsCaptor.capture(), + any(WifiNative.ScanEventHandler.class)); + assertNativeScanSettingsEquals(expected, scanSettingsCaptor.getValue()); + } + + private static final int MAX_AP_PER_SCAN = 16; private void startServiceAndLoadDriver() { mWifiScanningServiceImpl.startService(); when(mWifiScannerImpl.getScanCapabilities(any(WifiNative.ScanCapabilities.class))) @@ -158,7 +222,7 @@ public class WifiScanningServiceTest { public boolean answer(WifiNative.ScanCapabilities capabilities) { capabilities.max_scan_cache_size = Integer.MAX_VALUE; capabilities.max_scan_buckets = 8; - capabilities.max_ap_cache_per_scan = 16; + capabilities.max_ap_cache_per_scan = MAX_AP_PER_SCAN; capabilities.max_rssi_sample_size = 8; capabilities.max_scan_reporting_threshold = 10; capabilities.max_hotlist_bssids = 0; @@ -166,8 +230,12 @@ public class WifiScanningServiceTest { return true; } }); + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mContext) - .registerReceiver(mBroadcastReceiverCaptor.capture(), any(IntentFilter.class)); + .registerReceiver(broadcastReceiverCaptor.capture(), any(IntentFilter.class)); + mBroadcastReceiver = broadcastReceiverCaptor.getValue(); + sendWifiScanAvailable(WifiManager.WIFI_STATE_ENABLED); mLooper.dispatchAll(); } @@ -185,8 +253,10 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - sendAndAssertFailedBackgroundScan(controlChannel, handler, 122, generateValidScanSettings(), - WifiScanner.REASON_UNSPECIFIED, "not available"); + InOrder order = inOrder(handler); + sendBackgroundScanRequest(controlChannel, 122, generateValidScanSettings()); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, 122, WifiScanner.REASON_UNSPECIFIED, "not available"); } @Test @@ -196,10 +266,12 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler); when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), any(WifiNative.ScanEventHandler.class))).thenReturn(true); - sendAndAssertSuccessfulyBackgroundScan(controlChannel, handler, 192, - generateValidScanSettings()); + sendBackgroundScanRequest(controlChannel, 192, generateValidScanSettings()); + mLooper.dispatchAll(); + verifySuccessfulResponse(order, handler, 192); } @Test @@ -208,9 +280,368 @@ public class WifiScanningServiceTest { Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); - Message response = sendRequest(controlChannel, handler, - Message.obtain(null, Protocol.BASE_WIFI_MANAGER)); - assertFailedResponse(0, WifiScanner.REASON_INVALID_REQUEST, "Invalid request", response); + InOrder order = inOrder(handler, mWifiScannerImpl); + controlChannel.sendMessage(Message.obtain(null, Protocol.BASE_WIFI_MANAGER)); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, 0, WifiScanner.REASON_INVALID_REQUEST, + "Invalid request"); + } + + private void doSuccessfulSingleScan(WifiScanner.ScanSettings requestSettings, + WifiNative.ScanSettings nativeSettings, ScanResults results) { + int requestId = 12; + 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); + + sendSingleScanRequest(controlChannel, requestId, requestSettings); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order, nativeSettings); + verifySuccessfulResponse(order, handler, requestId); + + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results.getScanData()); + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, 12, results.getScanData()); + verifyNoMoreInteractions(handler); + } + + @Test + public void sendSingleScanBandRequest() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, 2400, 5150, 5175)); + } + + @Test + public void sendSingleScanChannelsRequest() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(2400), 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings), + ScanResults.create(0, 2400, 2400, 2400)); } + @Test + public void sendSingleScanRequestWhichFailsToStart() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 33; + + startServiceAndLoadDriver(); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + // scan fails + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(false); + + sendSingleScanRequest(controlChannel, requestId, requestSettings); + + mLooper.dispatchAll(); + // Scan is successfully queue, but then fails to execute + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + order.verify(handler, times(2)).handleMessage(messageCaptor.capture()); + assertSuccessfulResponse(requestId, messageCaptor.getAllValues().get(0)); + assertFailedResponse(requestId, WifiScanner.REASON_UNSPECIFIED, + "Failed to start single scan", messageCaptor.getAllValues().get(1)); + } + + @Test + public void sendSingleScanRequestWhichFailsAfterStart() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + int requestId = 33; + + startServiceAndLoadDriver(); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + // successful start + when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + sendSingleScanRequest(controlChannel, requestId, requestSettings); + + // Scan is successfully queue + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler = + verifyStartSingleScan(order, computeSingleScanNativeSettings(requestSettings)); + verifySuccessfulResponse(order, handler, requestId); + + // but then fails to execute + eventHandler.onScanStatus(WifiNative.WIFI_SCAN_DISABLED); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, requestId, + WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted"); + } + + // TODO Add more single scan tests + // * disable wifi while scanning + // * disable wifi while scanning with pending scan + + @Test + public void sendSingleScanRequestAfterPreviousCompletes() { + 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); + + + 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, requestId1, requestSettings1); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler1 = verifyStartSingleScan(order, + computeSingleScanNativeSettings(requestSettings1)); + verifySuccessfulResponse(order, handler, requestId1); + + // dispatch scan 1 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results1.getScanData()); + eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId1, results1.getScanData()); + + // Run scan 2 + sendSingleScanRequest(controlChannel, requestId2, requestSettings2); + + mLooper.dispatchAll(); + WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(order, + computeSingleScanNativeSettings(requestSettings2)); + verifySuccessfulResponse(order, handler, requestId2); + + // dispatch scan 2 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results2.getScanData()); + eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(order, handler, requestId2, results2.getScanData()); + } + + @Test + public void sendSingleScanRequestWhilePreviousScanRunning() { + 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); + + + 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); + + 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); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // 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()); + + // now that the first scan completed we expect the second one to start + WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(nativeOrder, + computeSingleScanNativeSettings(requestSettings2)); + + // dispatch scan 2 results + when(mWifiScannerImpl.getLatestSingleScanResults()) + .thenReturn(results2.getScanData()); + eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); + + mLooper.dispatchAll(); + verifyScanResultsRecieved(handlerOrder, handler, requestId2, results2.getScanData()); + } + + + + @Test + public void sendMultipleSingleScanRequestWhilePreviousScanRunning() { + 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 = createSingleScanNativeSettings( + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, channelsToSpec(2450, 5175, 5150)); + ScanResults results2and3 = ScanResults.merge(results2, results3); + + + 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); + + 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); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId2); + + // Queue scan 3 (will not run because previous is in progress) + sendSingleScanRequest(controlChannel, requestId3, requestSettings3); + mLooper.dispatchAll(); + verifySuccessfulResponse(handlerOrder, handler, requestId3); + + // 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()); + + // 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(); + + // 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(2)).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)); + assertScanResultsMessage(requestId3, + new WifiScanner.ScanData[] {results3.getScanData()}, + messageCaptor.getAllValues().get(1)); + } else { + assertScanResultsMessage(requestId3, + new WifiScanner.ScanData[] {results3.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(1)); + } + } + + private void doSuccessfulBackgroundScan(WifiScanner.ScanSettings requestSettings, + WifiNative.ScanSettings nativeSettings) { + startServiceAndLoadDriver(); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + InOrder order = inOrder(handler, mWifiScannerImpl); + + when(mWifiScannerImpl.startBatchedScan(any(WifiNative.ScanSettings.class), + any(WifiNative.ScanEventHandler.class))).thenReturn(true); + + sendBackgroundScanRequest(controlChannel, 12, requestSettings); + mLooper.dispatchAll(); + verifyStartBackgroundScan(order, nativeSettings); + verifySuccessfulResponse(order, handler, 12); + verifyNoMoreInteractions(handler); + } + + @Test + public void sendBackgroundScanBandRequest() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 20000, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = new NativeScanSettingsBuilder() + .withBasePeriod(20000) + .withMaxApPerScan(MAX_AP_PER_SCAN) + .withMaxScansToCache(WifiScanningScheduler.DEFAULT_MAX_SCANS_TO_BATCH) + .addBucketWithBand(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, + WifiScanner.WIFI_BAND_BOTH) + .build(); + doSuccessfulBackgroundScan(requestSettings, nativeSettings); + } + + @Test + public void sendBackgroundScanChannelsRequest() throws Exception { + WifiScanner.ScanSettings requestSettings = createRequest(channelsToSpec(5150), 20000, + 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); + WifiNative.ScanSettings nativeSettings = new NativeScanSettingsBuilder() + .withBasePeriod(20000) + .withMaxApPerScan(MAX_AP_PER_SCAN) + .withMaxScansToCache(WifiScanningScheduler.DEFAULT_MAX_SCANS_TO_BATCH) + .addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5150) + .build(); + doSuccessfulBackgroundScan(requestSettings, nativeSettings); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java index c092f0d79..096a7397e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -41,6 +41,7 @@ import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; +import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; @@ -73,6 +74,7 @@ import com.android.server.wifi.p2p.WifiP2pServiceImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -83,6 +85,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -192,6 +195,8 @@ public class WifiStateMachineTest { when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( mock(ConnectivityManager.class)); + when(context.getSystemService(Context.WIFI_SCANNING_SERVICE)).thenReturn(mWifiScanner); + return context; } @@ -256,16 +261,20 @@ public class WifiStateMachineTest { ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq, Long.MAX_VALUE /* needed so that scan results aren't rejected because there older than scan start */); + detail.getScanResult().informationElements = ie; + detail.getScanResult().anqpLines = new ArrayList<String>(); return detail; } - private ArrayList<ScanDetail> getMockScanResults() { + private ScanResult[] getMockScanResults() { ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825); - ArrayList<ScanDetail> list = sr.getScanDetailArrayList(); + // copy generated results and add space on the end for one more + ScanResult[] results = Arrays.copyOf(sr.getScanData().getResults(), + sr.getScanData().getResults().length + 1); int rssi = -65; - list.add(getGoogleGuestScanDetail(rssi)); - return list; + results[results.length - 1] = getGoogleGuestScanDetail(rssi).getScanResult(); + return results; } static final String sSSID = "\"GoogleGuest\""; @@ -283,6 +292,7 @@ public class WifiStateMachineTest { MockWifiMonitor mWifiMonitor; @Mock WifiNative mWifiNative; + @Mock WifiScanner mWifiScanner; @Mock SupplicantStateTracker mSupplicantStateTracker; @Mock WifiMetrics mWifiMetrics; @@ -510,14 +520,27 @@ public class WifiStateMachineTest { mWsm.startScan(-1, 0, null, null); wait(200); - verify(mWifiNative).scan(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); - - when(mWifiNative.getScanResults()).thenReturn(getMockScanResults()); - mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); + ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor = + ArgumentCaptor.forClass(WifiScanner.ScanSettings.class); + ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor = + ArgumentCaptor.forClass(WifiScanner.ScanListener.class); + verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture()); + assertEquals("band", WifiScanner.WIFI_BAND_BOTH_WITH_DFS, + scanSettingsCaptor.getValue().band); + assertEquals("reportEvents", WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, + scanSettingsCaptor.getValue().reportEvents); + + ScanResult[] results = getMockScanResults(); + for (ScanResult result : results) { + scanListenerCaptor.getValue().onFullResult(result); + } + scanListenerCaptor.getValue().onResults( + new WifiScanner.ScanData[] {new WifiScanner.ScanData(0, 0, results)}); wait(200); - List<ScanResult> results = mWsm.syncGetScanResultsList(); - assertEquals(8, results.size()); + List<ScanResult> reportedResults = mWsm.syncGetScanResultsList(); + assertEquals(8, reportedResults.size()); } @Test |