diff options
author | Mitchell Wills <mwills@google.com> | 2015-11-03 17:16:09 -0800 |
---|---|---|
committer | Roshan Pius <rpius@google.com> | 2016-03-09 20:05:08 -0800 |
commit | 8adb4e72f58e3e25918f33e0b2687e6acc14c47d (patch) | |
tree | 207b349128f3f634b883efe5c187a7191eb12fa4 /service | |
parent | 01fb5b27ba32f180080f2b2b12adbc5c803611ce (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
Bug: 26895774
Change-Id: I7d03b43bbd2be396b3c772bc4bae681acce8eb7b
Diffstat (limited to 'service')
6 files changed, 519 insertions, 157 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; |