diff options
14 files changed, 1100 insertions, 228 deletions
diff --git a/service/java/com/android/server/wifi/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/HalWifiScannerImpl.java index b86e4c6d8..246100b59 100644 --- a/service/java/com/android/server/wifi/HalWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/HalWifiScannerImpl.java @@ -58,14 +58,10 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb mChannelHelper = new HalChannelHelper(wifiNative); - // 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 diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java index 1f4024a2a..1dea556c1 100644 --- a/service/java/com/android/server/wifi/ScanDetail.java +++ b/service/java/com/android/server/wifi/ScanDetail.java @@ -80,6 +80,7 @@ public class ScanDetail { mScanResult = scanResult; mNetworkDetail = networkDetail; mMatches = matches; + mSeen = mScanResult.seen; } /** diff --git a/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java index b51317157..c1043f065 100644 --- a/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java +++ b/service/java/com/android/server/wifi/SupplicantWifiScannerImpl.java @@ -108,14 +108,10 @@ public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handle // TODO figure out how to get channel information from supplicant mChannelHelper = new NoBandChannelHelper(); - // 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 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 6263a9af3..9c8895440 100644 --- a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java @@ -40,18 +40,23 @@ import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; +import android.os.SystemClock; 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; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.internal.util.WakeupMessage; import com.android.server.wifi.scanner.ChannelHelper; +import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -175,7 +180,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) { - mStateMachine.sendMessage(Message.obtain(msg)); + mBackgroundScanStateMachine.sendMessage(Message.obtain(msg)); return; } ClientInfo ci = mClients.get(msg.replyTo); @@ -195,13 +200,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: + mBackgroundScanStateMachine.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; } } } @@ -218,8 +225,8 @@ 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_FAILED = BASE + 11; + private static final int CMD_SCAN_FAILED = BASE + 10; + private static final int CMD_SCAN_TIMEOUT = BASE + 11; private static final int CMD_PNO_NETWORK_FOUND = BASE + 12; private final Context mContext; @@ -231,9 +238,11 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private WifiScanningScheduler mScheduler; private WifiNative.ScanSettings mPreviousSchedule; - private WifiScanningStateMachine mStateMachine; + private WifiBackgroundScanStateMachine mBackgroundScanStateMachine; + private WifiSingleScanStateMachine mSingleScanStateMachine; private ClientHandler mClientHandler; private final IBatteryStats mBatteryStats; + private final AlarmManager mAlarmManager; WifiScanningServiceImpl(Context context, Looper looper, WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats) { @@ -242,14 +251,16 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mScannerImplFactory = scannerImplFactory; mBatteryStats = batteryStats; mClients = new ArrayMap<>(); + mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mPreviousSchedule = null; } public void startService() { mClientHandler = new ClientHandler(mLooper); - mStateMachine = new WifiScanningStateMachine(mLooper); + mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper); mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper); + mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper); mContext.registerReceiver( new BroadcastReceiver() { @@ -259,34 +270,392 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED); if (DBG) localLog("SCAN_AVAILABLE : " + state); if (state == WifiManager.WIFI_STATE_ENABLED) { - mStateMachine.sendMessage(CMD_DRIVER_LOADED); + mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED); + mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED); } else if (state == WifiManager.WIFI_STATE_DISABLED) { - mStateMachine.sendMessage(CMD_DRIVER_UNLOADED); + mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); + mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED); } } }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE)); - mStateMachine.start(); + mBackgroundScanStateMachine.start(); mWifiChangeStateMachine.start(); + mSingleScanStateMachine.start(); } - class WifiScanningStateMachine extends StateMachine implements WifiNative.ScanEventHandler, - WifiNative.PnoEventHandler, WifiNative.HotlistEventHandler, - WifiNative.SignificantWifiChangeEventHandler { + /** + * 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 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 IdleState. + */ + class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler { + private final DefaultState mDefaultState = new DefaultState(); + private final DriverStartedState mDriverStartedState = new DriverStartedState(); + private final IdleState mIdleState = new IdleState(); + private final ScanningState mScanningState = new ScanningState(); + + private ClientHandlerMap<ScanSettings> mActiveScans = new ClientHandlerMap<>(); + private ClientHandlerMap<ScanSettings> mPendingScans = new ClientHandlerMap<>(); + + WifiSingleScanStateMachine(Looper looper) { + super("WifiSingleScanStateMachine", looper); + + setLogRecSize(128); + setLogOnlyTransitions(false); + + // CHECKSTYLE:OFF IndentationCheck + addState(mDefaultState); + addState(mDriverStartedState, mDefaultState); + addState(mIdleState, 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_FAILED: + sendMessage(CMD_SCAN_FAILED); + 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(mIdleState); + 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 IdleState + * to hold common functionality and handle cleaning up scans when the driver is shut down. + */ + class DriverStartedState extends State { + @Override + public void exit() { + sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + } + + @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 a scan. Otherwise + // this scan will be scheduled when transitioning back to IdleState + // after finishing the current scan. + 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 IdleState extends State { + @Override + public void enter() { + tryToStartNewScan(); + } + + @Override + public boolean processMessage(Message msg) { + return NOT_HANDLED; + } + } + + class ScanningState extends State { + /** + * Duration to wait before timing out a scan. + * + * The expected behavior is that the hardware will return a failed scan if it does not + * complete, but timeout just in case it does not. + * It is possible that there may already be a scan in progress when the scan is + * scheduled, so make sure that there is time for two scans to complete. + */ + private static final long SINGLE_SCAN_TIMEOUT_MS = 20000; + private WakeupMessage mTimeoutMessage = new WakeupMessage(mContext, getHandler(), + "WifiScanner Single Scan Timeout", CMD_SCAN_TIMEOUT); + + @Override + public void enter() { + mTimeoutMessage.schedule( + SystemClock.elapsedRealtime() + SINGLE_SCAN_TIMEOUT_MS); + } + + @Override + public void exit() { + mTimeoutMessage.cancel(); + // if any scans are still active (never got results available then indicate failure) + sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED, + "Scan was interrupted"); + } + + // TODO(b/27247460) correctly handle battery blaming + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case CMD_SCAN_RESULTS_AVAILABLE: + reportScanResults(mScannerImpl.getLatestSingleScanResults()); + mActiveScans.clear(); + transitionTo(mIdleState); + return HANDLED; + case CMD_FULL_SCAN_RESULTS: + reportFullScanResult((ScanResult) msg.obj); + return HANDLED; + case CMD_SCAN_FAILED: + sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED, + "Scan failed"); + transitionTo(mIdleState); + return HANDLED; + case CMD_SCAN_TIMEOUT: + sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED, + "Timed out waiting for a scan result"); + loge("Timed out waiting for single scan result/failure"); + transitionTo(mIdleState); + 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; + } + if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + if (settings.channels == null || settings.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() { + if (mPendingScans.size() == 0) { // no pending requests + return; + } + mChannelHelper.updateChannels(); + // TODO move merging logic to a scheduler + WifiNative.ScanSettings settings = new WifiNative.ScanSettings(); + settings.num_buckets = 1; + WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings(); + bucketSettings.bucket = 0; + bucketSettings.period_ms = 0; + + ChannelCollection channels = mChannelHelper.createChannelCollection(); + HashSet<Integer> hiddenNetworkIdSet = new HashSet<>(); + for (WifiScanner.ScanSettings scanSettings : mPendingScans.values()) { + channels.addChannels(scanSettings); + if (scanSettings.hiddenNetworkIds != null) { + for (int i = 0; i < scanSettings.hiddenNetworkIds.length; i++) { + hiddenNetworkIdSet.add(scanSettings.hiddenNetworkIds[i]); + } + } + } + if (hiddenNetworkIdSet.size() > 0) { + settings.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()]; + int numHiddenNetworks = 0; + for (Integer hiddenNetworkId : hiddenNetworkIdSet) { + settings.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId; + } + } + + channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE); + bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + for (ScanSettings pendingRequest : mPendingScans.values()) { + if ((pendingRequest.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) + != 0) { + bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; + } + } + + 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 + sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED, + "Failed to start single scan"); + } + } + + void sendOpFailedToAllAndClear(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)); + } + clientHandlers.clear(); + } + + // TODO fix shouldReport checks + // currently these checks work because 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); + // make sure the handler is removed + ci.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, handler); + } + } + } + + class WifiBackgroundScanStateMachine extends StateMachine + implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler, + WifiNative.PnoEventHandler, WifiNative.SignificantWifiChangeEventHandler { private final DefaultState mDefaultState = new DefaultState(); private final StartedState mStartedState = new StartedState(); private final PausedState mPausedState = new PausedState(); - public WifiScanningStateMachine(Looper looper) { + WifiBackgroundScanStateMachine(Looper looper) { super(TAG, looper); setLogRecSize(512); setLogOnlyTransitions(false); + // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); addState(mStartedState, mDefaultState); addState(mPausedState, mDefaultState); + // CHECKSTYLE:ON IndentationCheck setInitialState(mDefaultState); } @@ -360,6 +729,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); } @@ -432,14 +804,14 @@ 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_START_PNO_SCAN: Bundle pnoParams = (Bundle) msg.obj; @@ -460,20 +832,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { 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); @@ -533,7 +891,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { case CMD_SCAN_FAILED: // TODO handle this gracefully (currently no implementations are know to // report this) - Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_TERMINATED"); + Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_FAILED"); break; case CMD_PNO_NETWORK_FOUND: { ScanResult[] results = (ScanResult[]) msg.obj; @@ -572,8 +930,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) { @@ -583,6 +947,18 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { if (DBG) localLog("New client, channel: " + c); } + void sendMessage(int what, int arg1, int arg2) { + if (!mDisconnected) { + mChannel.sendMessage(what, arg1, arg2); + } + } + + void sendMessage(int what, int arg1, int arg2, Object obj) { + if (!mDisconnected) { + mChannel.sendMessage(what, arg1, arg2, obj); + } + } + void reportBatchedScanStart() { if (mUid == 0) return; @@ -610,7 +986,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { // TODO migrate batterystats to accept scan duration per hour instead of csph int getCsph() { int totalScanDurationPerHour = 0; - for (ScanSettings settings : getScanSettings()) { + for (ScanSettings settings : getBackgroundScanSettings()) { int scanDurationMs = mChannelHelper.estimateScanDuration(settings); int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs; @@ -625,7 +1001,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { reportBatchedScanStop(); mScanWorkReported = false; } - if (mScanSettings.isEmpty() == false) { + if (mBackgroundScanSettings.isEmpty()) { reportBatchedScanStart(); mScanWorkReported = true; } @@ -633,14 +1009,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"); @@ -653,33 +1030,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); @@ -687,7 +1057,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); @@ -696,11 +1066,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[]) { @@ -710,10 +1075,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) { @@ -728,18 +1093,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) { @@ -841,7 +1206,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } void cleanup() { - mScanSettings.clear(); + mDisconnected = true; + mBackgroundScanSettings.clear(); updateSchedule(); mHotlistSettings.clear(); @@ -891,7 +1257,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mChannelHelper.updateChannels(); ArrayList<ScanSettings> settings = new ArrayList<>(); for (ClientInfo client : mClients.values()) { - settings.addAll(client.getScanSettings()); + settings.addAll(client.getBackgroundScanSettings()); } mScheduler.updateSchedule(settings); @@ -920,7 +1286,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { + ChannelHelper.toString(bucket)); } - if (mScannerImpl.startBatchedScan(schedule, mStateMachine)) { + if (mScannerImpl.startBatchedScan(schedule, mBackgroundScanStateMachine)) { if (DBG) Log.d(TAG, "scan restarted with " + schedule.num_buckets + " bucket(s) and base period: " + schedule.base_period_ms); return true; @@ -957,7 +1323,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); @@ -1006,13 +1372,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } - logScanRequest("addScanRequest", ci, handler, settings); - ci.addScanRequest(settings, handler); + logScanRequest("addBackgroundScanRequest", ci, handler, settings); + ci.addBackgroundScanRequest(settings, handler); if (updateSchedule()) { return true; } else { - ci.removeScanRequest(handler); + ci.removeBackgroundScanRequest(handler); localLog("Failing scan request because failed to reset scan"); return false; } @@ -1042,13 +1408,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { boolean addScanRequestForPno(ClientInfo ci, int handler, ScanSettings settings, PnoSettings pnoSettings) { - if (!mScannerImpl.setPnoList(convertPnoSettingsToNative(pnoSettings), mStateMachine)) { + if (!mScannerImpl.setPnoList(convertPnoSettingsToNative(pnoSettings), + mBackgroundScanStateMachine)) { return false; } if (!mScannerImpl.shouldScheduleBackgroundScanForPno()) { return true; } - return addScanRequest(ci, handler, settings); + return addBackgroundScanRequest(ci, handler, settings); } void removeScanRequestForPno(ClientInfo ci, int handler, PnoSettings pnoSettings) { @@ -1056,39 +1423,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { if (!mScannerImpl.shouldScheduleBackgroundScanForPno()) { return; } - removeScanRequest(ci, handler); + removeBackgroundScanRequest(ci, handler); } - 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); - - if (updateSchedule()) { - /* reset periodInMs to 0 to indicate single shot scan */ - settings.periodInMs = 0; - return true; - } else { - ci.removeScanRequest(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(); } } @@ -1144,7 +1485,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { WifiScanner.HotlistSettings settings = new WifiScanner.HotlistSettings(); settings.bssidInfos = bssidInfos; settings.apLostThreshold = apLostThreshold; - mScannerImpl.setHotlist(settings, mStateMachine); + mScannerImpl.setHotlist(settings, mBackgroundScanStateMachine); } } @@ -1229,7 +1570,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final String ACTION_TIMEOUT = "com.android.server.WifiScanningServiceImpl.action.TIMEOUT"; - AlarmManager mAlarmManager; PendingIntent mTimeoutIntent; ScanResult mCurrentBssids[]; @@ -1238,18 +1578,16 @@ 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); } public void enable() { - if (mAlarmManager == null) { - mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - } - if (mTimeoutIntent == null) { Intent intent = new Intent(ACTION_TIMEOUT, null); mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); @@ -1640,7 +1978,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 9519d80ca..bbca61123 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -111,6 +111,7 @@ import com.android.server.wifi.hotspot2.IconEvent; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.hotspot2.Utils; import com.android.server.wifi.p2p.WifiP2pServiceImpl; +import com.android.server.wifi.util.ScanDetailUtil; import java.io.BufferedReader; import java.io.FileDescriptor; @@ -2072,17 +2073,52 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven } + // TODO this is a temporary measure to bridge between WifiScanner and WifiStateMachine until + // scan functionality is refactored out of WifiStateMachine. /** * return true iff scan request is accepted */ - private boolean startScanNative(Set<Integer> freqs, Set<Integer> hiddenNetworkIds) { - if (mWifiNative.scan(freqs, hiddenNetworkIds)) { - mIsScanOngoing = true; - mIsFullScanOngoing = (freqs == null); - lastScanFreqs = freqs; - return true; + private boolean startScanNative(final Set<Integer> freqs, Set<Integer> hiddenNetworkIds) { + 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; + if (hiddenNetworkIds != null && hiddenNetworkIds.size() > 0) { + int i = 0; + settings.hiddenNetworkIds = new int[hiddenNetworkIds.size()]; + for (Integer netId : hiddenNetworkIds) { + settings.hiddenNetworkIds[i++] = netId; + } + } + WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() { + // ignore all events since WifiStateMachine is registered for the supplicant events + public void onSuccess() { + } + public void onFailure(int reason, String description) { + mIsScanOngoing = false; + mIsFullScanOngoing = false; + } + public void onResults(WifiScanner.ScanData[] results) { + } + public void onFullResult(ScanResult fullScanResult) { + } + public void onPeriodChanged(int periodInMs) { + } + }; + mWifiScanner.startScan(settings, nativeScanListener); + mIsScanOngoing = true; + mIsFullScanOngoing = (freqs == null); + lastScanFreqs = freqs; + return true; } /** @@ -4494,12 +4530,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); } - void connectScanningService() { - if (mWifiScanner == null) { - mWifiScanner = (WifiScanner) mContext.getSystemService(Context.WIFI_SCANNING_SERVICE); - } - } - private void handleIPv4Success(DhcpResults dhcpResults) { if (DBG) { logd("handleIPv4Success <" + dhcpResults.toString() + ">"); @@ -5564,11 +5594,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven class DriverStartedState extends State { @Override public void enter() { - if (DBG) { 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(DBG); mIsRunning = true; updateBatteryWorkSource(null); @@ -6194,11 +6230,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven class ConnectModeState extends State { @Override - public void enter() { - connectScanningService(); - } - - @Override public boolean processMessage(Message message) { WifiConfiguration config; int netId; diff --git a/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java b/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java index baf317280..6d4edb40d 100644 --- a/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/BaseWifiScannerImplTest.java @@ -199,7 +199,6 @@ public abstract class BaseWifiScannerImplTest { WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class); ScanResults results = ScanResults.create(0, 2400, 2450, 2450); - Set<Integer> expectedScan = createFreqSet(2400, 2450); InOrder order = inOrder(eventHandler, mWifiNative); diff --git a/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java index 3ef073ce6..11acea80d 100644 --- a/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/HalWifiScannerTest.java @@ -18,13 +18,10 @@ package com.android.server.wifi; import static com.android.server.wifi.ScanTestUtil.setupMockChannels; -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}. */ @@ -38,14 +35,5 @@ public class HalWifiScannerTest extends BaseWifiScannerImplTest { new int[]{5150, 5175}, new int[]{5600, 5650}); mScanner = new HalWifiScannerImpl(mWifiNative, 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 f0e8e95b8..86db34cb6 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; @@ -465,36 +467,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); - assertEquals("channels", getAllChannels(settings), - getAllChannels(schedule.buckets[0])); - } - 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, + settings.channels); + } 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 f628cac93..9c452fb21 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanResults.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanResults.java @@ -26,6 +26,7 @@ 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; @@ -38,6 +39,32 @@ 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; + } + + /** + * Merge the results contained in a number of ScanResults into a single 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), diff --git a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java index de46dff42..34793cd8f 100644 --- a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java @@ -171,6 +171,42 @@ public class ScanTestUtil { } } + /** + * Compute the expected native scan settings that are expected for the given + * WifiScanner.ScanSettings. + */ + public static WifiNative.ScanSettings computeSingleScanNativeSettings( + WifiScanner.ScanSettings requestSettings) { + int reportEvents = requestSettings.reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + NativeScanSettingsBuilder builder = new NativeScanSettingsBuilder() + .withBasePeriod(0) + .withMaxApPerScan(0) + .withMaxPercentToCache(0) + .withMaxScansToCache(0); + if (requestSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { + builder.addBucketWithChannels(0, reportEvents, requestSettings.channels); + } else { + builder.addBucketWithBand(0, reportEvents, requestSettings.band); + } + + return builder.build(); + } + + /** + * Compute the expected native scan settings that are expected for the given channels. + */ + public static WifiNative.ScanSettings createSingleScanNativeSettingsForChannels( + int reportEvents, WifiScanner.ChannelSpec... channels) { + int actualReportEvents = reportEvents | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; + return new NativeScanSettingsBuilder() + .withBasePeriod(0) + .withMaxApPerScan(0) + .withMaxPercentToCache(0) + .withMaxScansToCache(0) + .addBucketWithChannels(0, actualReportEvents, channels) + .build(); + } + public static Set<Integer> createFreqSet(int... elements) { Set<Integer> set = new HashSet<>(); for (int e : elements) { diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java index 72e9da5ec..f3adb119e 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantWifiScannerTest.java @@ -31,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.HashSet; import java.util.Set; @@ -53,15 +51,6 @@ public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest { public void setup() throws Exception { mScanner = new SupplicantWifiScannerImpl(mContext, mWifiNative, 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 diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java index 853c895bd..3d4055a52 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiScanningServiceTest.java @@ -16,13 +16,19 @@ 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.createSingleScanNativeSettingsForChannels; 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.argThat; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -54,6 +60,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.internal.matchers.CapturingMatcher; /** * Unit tests for {@link com.android.server.wifi.WifiScanningServiceImpl}. @@ -63,6 +70,7 @@ public class WifiScanningServiceTest { public static final String TAG = "WifiScanningServiceTest"; @Mock Context mContext; + MockAlarmManager mAlarmManager; @Mock WifiScannerImpl mWifiScannerImpl; @Mock WifiScannerImpl.WifiScannerImplFactory mWifiScannerImplFactory; @Mock IBatteryStats mBatteryStats; @@ -73,6 +81,10 @@ public class WifiScanningServiceTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mAlarmManager = new MockAlarmManager(); + when(mContext.getSystemService(Context.ALARM_SERVICE)) + .thenReturn(mAlarmManager.getAlarmManager()); + ChannelHelper channelHelper = new PresetKnownBandsChannelHelper( new int[]{2400, 2450}, new int[]{5150, 5175}, @@ -117,9 +129,22 @@ public class WifiScanningServiceTest { return messageCaptor.getValue(); } + private Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler, + final int what) { + CapturingMatcher<Message> messageMatcher = new CapturingMatcher<Message>() { + public boolean matches(Object argument) { + Message message = (Message) argument; + return message.what == what; + } + }; + order.verify(handler).handleMessage(argThat(messageMatcher)); + return messageMatcher.getLastValue(); + } + private void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId, WifiScanner.ScanData... expected) { - Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler); + Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler, + WifiScanner.CMD_SCAN_RESULT); assertScanResultsMessage(listenerId, expected, scanResultMessage); } @@ -131,12 +156,29 @@ public class WifiScanningServiceTest { ((WifiScanner.ParcelableScanData) scanResultMessage.obj).getResults()); } + private void verifySingleScanCompletedRecieved(InOrder order, Handler handler, int listenerId) { + Message completedMessage = verifyHandleMessageAndGetMessage(order, handler, + WifiScanner.CMD_SINGLE_SCAN_COMPLETED); + assertSingleScanCompletedMessage(listenerId, completedMessage); + } + + private void assertSingleScanCompletedMessage(int listenerId, Message completedMessage) { + assertEquals("what", WifiScanner.CMD_SINGLE_SCAN_COMPLETED, completedMessage.what); + assertEquals("listenerId", listenerId, completedMessage.arg2); + } + 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); @@ -174,6 +216,18 @@ public class WifiScanningServiceTest { } } + 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); @@ -182,6 +236,7 @@ public class WifiScanningServiceTest { 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))) @@ -189,7 +244,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; @@ -209,7 +264,7 @@ public class WifiScanningServiceTest { @Test public void construct() throws Exception { - verifyNoMoreInteractions(mContext, mWifiScannerImpl, mWifiScannerImpl, + verifyNoMoreInteractions(mWifiScannerImpl, mWifiScannerImpl, mWifiScannerImplFactory, mBatteryStats); } @@ -253,4 +308,403 @@ public class WifiScanningServiceTest { 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, requestId, results.getScanData()); + verifySingleScanCompletedRecieved(order, handler, requestId); + verifyNoMoreInteractions(handler); + } + + /** + * Do a single scan for a band and verify that it is successful. + */ + @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)); + } + + /** + * Do a single scan for a list of channels and verify that it is successful. + */ + @Test + public void sendSingleScanChannelsRequest() 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)); + } + + /** + * Do a single scan, which the hardware fails to start, and verify that a failure response is + * delivered. + */ + @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)); + } + + /** + * Do a single scan, which successfully starts, but fails partway through and verify that a + * failure response is delivered. + */ + @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_FAILED); + mLooper.dispatchAll(); + verifyFailedResponse(order, handler, requestId, + WifiScanner.REASON_UNSPECIFIED, "Scan failed"); + } + + // TODO Add more single scan tests + // * disable wifi while scanning + // * disable wifi while scanning with pending scan + + /** + * Send a single scan request and then a second one after the first completes. + */ + @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()); + verifySingleScanCompletedRecieved(order, handler, requestId1); + + // 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()); + verifySingleScanCompletedRecieved(order, handler, requestId2); + } + + /** + * Send a single scan request and then a second one before the first completes. + * Verify that both are scheduled and succeed. + */ + @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()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1); + + // 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()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId2); + } + + + + /** + * Send a single scan request and then two more before the first completes. + * Verify that the first completes and the second two are merged. + */ + @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 = createSingleScanNativeSettingsForChannels( + 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()); + verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1); + + // 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(4)).handleMessage(messageCaptor.capture()); + int firstListenerId = messageCaptor.getAllValues().get(0).arg2; + assertTrue(firstListenerId + " was neither " + requestId2 + " nor " + requestId3, + firstListenerId == requestId2 || firstListenerId == requestId3); + if (firstListenerId == requestId2) { + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId3, + new WifiScanner.ScanData[] {results3.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(3)); + } else { + assertScanResultsMessage(requestId3, + new WifiScanner.ScanData[] {results3.getScanData()}, + messageCaptor.getAllValues().get(0)); + assertSingleScanCompletedMessage(requestId3, messageCaptor.getAllValues().get(1)); + assertScanResultsMessage(requestId2, + new WifiScanner.ScanData[] {results2.getScanData()}, + messageCaptor.getAllValues().get(2)); + assertSingleScanCompletedMessage(requestId2, messageCaptor.getAllValues().get(3)); + } + } + + 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); + } + + /** + * Do a background scan for a band and verify that it is successful. + */ + @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); + } + + /** + * Do a background scan for a list of channels and verify that it is successful. + */ + @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 f868c522b..2663b0c8e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -45,6 +45,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; @@ -81,6 +82,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.MockitoAnnotations; @@ -92,8 +94,10 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Unit tests for {@link com.android.server.wifi.WifiStateMachine}. @@ -234,6 +238,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; } @@ -281,7 +287,7 @@ 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 */ - null, null); + ie, new ArrayList<String>()); return detail; } @@ -312,6 +318,7 @@ public class WifiStateMachineTest { WifiConfigManager mWifiConfigManager; @Mock WifiNative mWifiNative; + @Mock WifiScanner mWifiScanner; @Mock SupplicantStateTracker mSupplicantStateTracker; @Mock WifiMetrics mWifiMetrics; @Mock UserManager mUserManager; @@ -743,6 +750,36 @@ public class WifiStateMachineTest { forgetNetworkAndVerifyFailure(); } + private void verifyScan(int band, int reportEvents, Set<Integer> configuredNetworkIds) { + 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()); + WifiScanner.ScanSettings actualSettings = scanSettingsCaptor.getValue(); + assertEquals("band", band, actualSettings.band); + assertEquals("reportEvents", reportEvents, actualSettings.reportEvents); + + if (configuredNetworkIds == null) { + configuredNetworkIds = new HashSet<>(); + } + Set<Integer> actualConfiguredNetworkIds = new HashSet<>(); + if (actualSettings.hiddenNetworkIds != null) { + for (int i = 0; i < actualSettings.hiddenNetworkIds.length; ++i) { + actualConfiguredNetworkIds.add(actualSettings.hiddenNetworkIds[i]); + } + } + assertEquals("configured networks", configuredNetworkIds, actualConfiguredNetworkIds); + + when(mWifiNative.getScanResults()).thenReturn(getMockScanResults()); + mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); + + mLooper.dispatchAll(); + + List<ScanResult> reportedResults = mWsm.syncGetScanResultsList(); + assertEquals(8, reportedResults.size()); + } + @Test public void scan() throws Exception { addNetworkAndVerifySuccess(); @@ -751,14 +788,9 @@ public class WifiStateMachineTest { mWsm.startScan(-1, 0, null, null); mLooper.dispatchAll(); - verify(mWifiNative).scan(null, mWifiConfigManager.getHiddenConfiguredNetworkIds()); - - when(mWifiNative.getScanResults()).thenReturn(getMockScanResults()); - mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); - mLooper.dispatchAll(); - - List<ScanResult> results = mWsm.syncGetScanResultsList(); - assertEquals(8, results.size()); + verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, null); } @Test @@ -769,14 +801,10 @@ public class WifiStateMachineTest { mWsm.startScan(-1, 0, null, null); mLooper.dispatchAll(); - verify(mWifiNative).scan(null, mWifiConfigManager.getHiddenConfiguredNetworkIds()); - - when(mWifiNative.getScanResults()).thenReturn(getMockScanResults()); - mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); - mLooper.dispatchAll(); - - List<ScanResult> results = mWsm.syncGetScanResultsList(); - assertEquals(8, results.size()); + verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS, + WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN + | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, + mWifiConfigManager.getHiddenConfiguredNetworkIds()); } @Test |