1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.scanner; 18 19 import android.app.AlarmManager; 20 import android.content.Context; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.WifiScanner; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.util.Log; 27 28 import com.android.internal.R; 29 import com.android.server.wifi.Clock; 30 import com.android.server.wifi.ScanDetail; 31 import com.android.server.wifi.WifiMonitor; 32 import com.android.server.wifi.WifiNative; 33 import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; 34 35 import java.util.ArrayDeque; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collections; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Set; 42 43 /** 44 * Implementation of the WifiScanner HAL API that uses wificond to perform all scans 45 * @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method. 46 */ 47 public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback { 48 private static final String TAG = "WificondScannerImpl"; 49 private static final boolean DBG = false; 50 51 public static final String BACKGROUND_PERIOD_ALARM_TAG = TAG + " Background Scan Period"; 52 public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout"; 53 // Max number of networks that can be specified to wificond per scan request 54 public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16; 55 56 private static final int SCAN_BUFFER_CAPACITY = 10; 57 private static final int MAX_APS_PER_SCAN = 32; 58 private static final int MAX_SCAN_BUCKETS = 16; 59 60 private final Context mContext; 61 private final WifiNative mWifiNative; 62 private final AlarmManager mAlarmManager; 63 private final Handler mEventHandler; 64 private final ChannelHelper mChannelHelper; 65 private final Clock mClock; 66 67 private final Object mSettingsLock = new Object(); 68 69 // Next scan settings to apply when the previous scan completes 70 private WifiNative.ScanSettings mPendingBackgroundScanSettings = null; 71 private WifiNative.ScanEventHandler mPendingBackgroundScanEventHandler = null; 72 private WifiNative.ScanSettings mPendingSingleScanSettings = null; 73 private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null; 74 75 // Active background scan settings/state 76 private WifiNative.ScanSettings mBackgroundScanSettings = null; 77 private WifiNative.ScanEventHandler mBackgroundScanEventHandler = null; 78 private int mNextBackgroundScanPeriod = 0; 79 private int mNextBackgroundScanId = 0; 80 private boolean mBackgroundScanPeriodPending = false; 81 private boolean mBackgroundScanPaused = false; 82 private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY); 83 84 private WifiScanner.ScanData mLatestSingleScanResult = 85 new WifiScanner.ScanData(0, 0, new ScanResult[0]); 86 87 // Settings for the currently running scan, null if no scan active 88 private LastScanSettings mLastScanSettings = null; 89 90 // Pno related info. 91 private WifiNative.PnoSettings mPnoSettings = null; 92 private WifiNative.PnoEventHandler mPnoEventHandler; 93 private final boolean mHwPnoScanSupported; 94 private final HwPnoDebouncer mHwPnoDebouncer; 95 private final HwPnoDebouncer.Listener mHwPnoDebouncerListener = new HwPnoDebouncer.Listener() { 96 public void onPnoScanFailed() { 97 Log.e(TAG, "Pno scan failure received"); 98 reportPnoScanFailure(); 99 } 100 }; 101 102 /** 103 * Duration to wait before timing out a scan. 104 * 105 * The expected behavior is that the hardware will return a failed scan if it does not 106 * complete, but timeout just in case it does not. 107 */ 108 private static final long SCAN_TIMEOUT_MS = 15000; 109 110 AlarmManager.OnAlarmListener mScanPeriodListener = new AlarmManager.OnAlarmListener() { 111 public void onAlarm() { 112 synchronized (mSettingsLock) { 113 handleScanPeriod(); 114 } 115 } 116 }; 117 118 AlarmManager.OnAlarmListener mScanTimeoutListener = new AlarmManager.OnAlarmListener() { 119 public void onAlarm() { 120 synchronized (mSettingsLock) { 121 handleScanTimeout(); 122 } 123 } 124 }; 125 126 public WificondScannerImpl(Context context, WifiNative wifiNative, 127 WifiMonitor wifiMonitor, ChannelHelper channelHelper, 128 Looper looper, Clock clock) { 129 mContext = context; 130 mWifiNative = wifiNative; 131 mChannelHelper = channelHelper; 132 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 133 mEventHandler = new Handler(looper, this); 134 mClock = clock; 135 mHwPnoDebouncer = new HwPnoDebouncer(mWifiNative, mAlarmManager, mEventHandler, mClock); 136 137 // Check if the device supports HW PNO scans. 138 mHwPnoScanSupported = mContext.getResources().getBoolean( 139 R.bool.config_wifi_background_scan_support); 140 141 wifiMonitor.registerHandler(mWifiNative.getInterfaceName(), 142 WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); 143 wifiMonitor.registerHandler(mWifiNative.getInterfaceName(), 144 WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler); 145 wifiMonitor.registerHandler(mWifiNative.getInterfaceName(), 146 WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); 147 } 148 149 public WificondScannerImpl(Context context, WifiNative wifiNative, 150 WifiMonitor wifiMonitor, Looper looper, Clock clock) { 151 // TODO get channel information from wificond. 152 this(context, wifiNative, wifiMonitor, new NoBandChannelHelper(), looper, clock); 153 } 154 155 @Override 156 public void cleanup() { 157 synchronized (mSettingsLock) { 158 mPendingSingleScanSettings = null; 159 mPendingSingleScanEventHandler = null; 160 stopHwPnoScan(); 161 stopBatchedScan(); 162 mLastScanSettings = null; // finally clear any active scan 163 } 164 } 165 166 @Override 167 public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) { 168 capabilities.max_scan_cache_size = Integer.MAX_VALUE; 169 capabilities.max_scan_buckets = MAX_SCAN_BUCKETS; 170 capabilities.max_ap_cache_per_scan = MAX_APS_PER_SCAN; 171 capabilities.max_rssi_sample_size = 8; 172 capabilities.max_scan_reporting_threshold = SCAN_BUFFER_CAPACITY; 173 return true; 174 } 175 176 @Override 177 public ChannelHelper getChannelHelper() { 178 return mChannelHelper; 179 } 180 181 @Override 182 public boolean startSingleScan(WifiNative.ScanSettings settings, 183 WifiNative.ScanEventHandler eventHandler) { 184 if (eventHandler == null || settings == null) { 185 Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings 186 + ",eventHandler=" + eventHandler); 187 return false; 188 } 189 if (mPendingSingleScanSettings != null 190 || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) { 191 Log.w(TAG, "A single scan is already running"); 192 return false; 193 } 194 synchronized (mSettingsLock) { 195 mPendingSingleScanSettings = settings; 196 mPendingSingleScanEventHandler = eventHandler; 197 processPendingScans(); 198 return true; 199 } 200 } 201 202 @Override 203 public WifiScanner.ScanData getLatestSingleScanResults() { 204 return mLatestSingleScanResult; 205 } 206 207 @Override 208 public boolean startBatchedScan(WifiNative.ScanSettings settings, 209 WifiNative.ScanEventHandler eventHandler) { 210 if (settings == null || eventHandler == null) { 211 Log.w(TAG, "Invalid arguments for startBatched: settings=" + settings 212 + ",eventHandler=" + eventHandler); 213 return false; 214 } 215 216 if (settings.max_ap_per_scan < 0 || settings.max_ap_per_scan > MAX_APS_PER_SCAN) { 217 return false; 218 } 219 if (settings.num_buckets < 0 || settings.num_buckets > MAX_SCAN_BUCKETS) { 220 return false; 221 } 222 if (settings.report_threshold_num_scans < 0 223 || settings.report_threshold_num_scans > SCAN_BUFFER_CAPACITY) { 224 return false; 225 } 226 if (settings.report_threshold_percent < 0 || settings.report_threshold_percent > 100) { 227 return false; 228 } 229 if (settings.base_period_ms <= 0) { 230 return false; 231 } 232 for (int i = 0; i < settings.num_buckets; ++i) { 233 WifiNative.BucketSettings bucket = settings.buckets[i]; 234 if (bucket.period_ms % settings.base_period_ms != 0) { 235 return false; 236 } 237 } 238 239 synchronized (mSettingsLock) { 240 stopBatchedScan(); 241 if (DBG) { 242 Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period=" 243 + settings.base_period_ms + " ms"); 244 } 245 mPendingBackgroundScanSettings = settings; 246 mPendingBackgroundScanEventHandler = eventHandler; 247 handleScanPeriod(); // Try to start scan immediately 248 return true; 249 } 250 } 251 252 @Override 253 public void stopBatchedScan() { 254 synchronized (mSettingsLock) { 255 if (DBG) Log.d(TAG, "Stopping scan"); 256 mBackgroundScanSettings = null; 257 mBackgroundScanEventHandler = null; 258 mPendingBackgroundScanSettings = null; 259 mPendingBackgroundScanEventHandler = null; 260 mBackgroundScanPaused = false; 261 mBackgroundScanPeriodPending = false; 262 unscheduleScansLocked(); 263 } 264 processPendingScans(); 265 } 266 267 @Override 268 public void pauseBatchedScan() { 269 synchronized (mSettingsLock) { 270 if (DBG) Log.d(TAG, "Pausing scan"); 271 // if there isn't a pending scan then make the current scan pending 272 if (mPendingBackgroundScanSettings == null) { 273 mPendingBackgroundScanSettings = mBackgroundScanSettings; 274 mPendingBackgroundScanEventHandler = mBackgroundScanEventHandler; 275 } 276 mBackgroundScanSettings = null; 277 mBackgroundScanEventHandler = null; 278 mBackgroundScanPeriodPending = false; 279 mBackgroundScanPaused = true; 280 281 unscheduleScansLocked(); 282 283 WifiScanner.ScanData[] results = getLatestBatchedScanResults(/* flush = */ true); 284 if (mPendingBackgroundScanEventHandler != null) { 285 mPendingBackgroundScanEventHandler.onScanPaused(results); 286 } 287 } 288 processPendingScans(); 289 } 290 291 @Override 292 public void restartBatchedScan() { 293 synchronized (mSettingsLock) { 294 if (DBG) Log.d(TAG, "Restarting scan"); 295 if (mPendingBackgroundScanEventHandler != null) { 296 mPendingBackgroundScanEventHandler.onScanRestarted(); 297 } 298 mBackgroundScanPaused = false; 299 handleScanPeriod(); 300 } 301 } 302 303 private void unscheduleScansLocked() { 304 mAlarmManager.cancel(mScanPeriodListener); 305 if (mLastScanSettings != null) { 306 mLastScanSettings.backgroundScanActive = false; 307 } 308 } 309 310 private void handleScanPeriod() { 311 synchronized (mSettingsLock) { 312 mBackgroundScanPeriodPending = true; 313 processPendingScans(); 314 } 315 } 316 317 private void handleScanTimeout() { 318 Log.e(TAG, "Timed out waiting for scan result from wificond"); 319 reportScanFailure(); 320 processPendingScans(); 321 } 322 323 private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) { 324 return (mLastScanSettings == null || !Arrays.equals( 325 newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList)); 326 } 327 328 private void processPendingScans() { 329 synchronized (mSettingsLock) { 330 // Wait for the active scan result to come back to reschedule other scans, 331 // unless if HW pno scan is running. Hw PNO scans are paused it if there 332 // are other pending scans, 333 if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) { 334 return; 335 } 336 337 ChannelCollection allFreqs = mChannelHelper.createChannelCollection(); 338 Set<String> hiddenNetworkSSIDSet = new HashSet<>(); 339 final LastScanSettings newScanSettings = 340 new LastScanSettings(mClock.getElapsedSinceBootMillis()); 341 342 // Update scan settings if there is a pending scan 343 if (!mBackgroundScanPaused) { 344 if (mPendingBackgroundScanSettings != null) { 345 mBackgroundScanSettings = mPendingBackgroundScanSettings; 346 mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler; 347 mNextBackgroundScanPeriod = 0; 348 mPendingBackgroundScanSettings = null; 349 mPendingBackgroundScanEventHandler = null; 350 mBackgroundScanPeriodPending = true; 351 } 352 if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) { 353 int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch 354 for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets; 355 ++bucket_id) { 356 WifiNative.BucketSettings bucket = 357 mBackgroundScanSettings.buckets[bucket_id]; 358 if (mNextBackgroundScanPeriod % (bucket.period_ms 359 / mBackgroundScanSettings.base_period_ms) == 0) { 360 if ((bucket.report_events 361 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { 362 reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 363 } 364 if ((bucket.report_events 365 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 366 reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 367 } 368 // only no batch if all buckets specify it 369 if ((bucket.report_events 370 & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 371 reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; 372 } 373 374 allFreqs.addChannels(bucket); 375 } 376 } 377 if (!allFreqs.isEmpty()) { 378 newScanSettings.setBackgroundScan(mNextBackgroundScanId++, 379 mBackgroundScanSettings.max_ap_per_scan, reportEvents, 380 mBackgroundScanSettings.report_threshold_num_scans, 381 mBackgroundScanSettings.report_threshold_percent); 382 } 383 mNextBackgroundScanPeriod++; 384 mBackgroundScanPeriodPending = false; 385 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 386 mClock.getElapsedSinceBootMillis() 387 + mBackgroundScanSettings.base_period_ms, 388 BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler); 389 } 390 } 391 392 if (mPendingSingleScanSettings != null) { 393 boolean reportFullResults = false; 394 ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection(); 395 for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) { 396 WifiNative.BucketSettings bucketSettings = 397 mPendingSingleScanSettings.buckets[i]; 398 if ((bucketSettings.report_events 399 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 400 reportFullResults = true; 401 } 402 singleScanFreqs.addChannels(bucketSettings); 403 allFreqs.addChannels(bucketSettings); 404 } 405 newScanSettings.setSingleScan(reportFullResults, singleScanFreqs, 406 mPendingSingleScanEventHandler); 407 408 WifiNative.HiddenNetwork[] hiddenNetworks = 409 mPendingSingleScanSettings.hiddenNetworks; 410 if (hiddenNetworks != null) { 411 int numHiddenNetworks = 412 Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN); 413 for (int i = 0; i < numHiddenNetworks; i++) { 414 hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid); 415 } 416 } 417 418 mPendingSingleScanSettings = null; 419 mPendingSingleScanEventHandler = null; 420 } 421 422 if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) 423 && !allFreqs.isEmpty()) { 424 pauseHwPnoScan(); 425 Set<Integer> freqs = allFreqs.getScanFreqs(); 426 boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); 427 if (success) { 428 // TODO handle scan timeout 429 if (DBG) { 430 Log.d(TAG, "Starting wifi scan for freqs=" + freqs 431 + ", background=" + newScanSettings.backgroundScanActive 432 + ", single=" + newScanSettings.singleScanActive); 433 } 434 mLastScanSettings = newScanSettings; 435 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 436 mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, 437 TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); 438 } else { 439 Log.e(TAG, "Failed to start scan, freqs=" + freqs); 440 // indicate scan failure async 441 mEventHandler.post(new Runnable() { 442 public void run() { 443 if (newScanSettings.singleScanEventHandler != null) { 444 newScanSettings.singleScanEventHandler 445 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 446 } 447 } 448 }); 449 // TODO(b/27769665) background scans should be failed too if scans fail enough 450 } 451 } else if (isHwPnoScanRequired()) { 452 newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler); 453 boolean status; 454 // If the PNO network list has changed from the previous request, ensure that 455 // we bypass the debounce logic and restart PNO scan. 456 if (isDifferentPnoScanSettings(newScanSettings)) { 457 status = restartHwPnoScan(mPnoSettings); 458 } else { 459 status = startHwPnoScan(mPnoSettings); 460 } 461 if (status) { 462 mLastScanSettings = newScanSettings; 463 } else { 464 Log.e(TAG, "Failed to start PNO scan"); 465 // indicate scan failure async 466 mEventHandler.post(new Runnable() { 467 public void run() { 468 if (mPnoEventHandler != null) { 469 mPnoEventHandler.onPnoScanFailed(); 470 } 471 // Clean up PNO state, we don't want to continue PNO scanning. 472 mPnoSettings = null; 473 mPnoEventHandler = null; 474 } 475 }); 476 } 477 } 478 } 479 } 480 481 @Override 482 public boolean handleMessage(Message msg) { 483 switch(msg.what) { 484 case WifiMonitor.SCAN_FAILED_EVENT: 485 Log.w(TAG, "Scan failed"); 486 mAlarmManager.cancel(mScanTimeoutListener); 487 reportScanFailure(); 488 processPendingScans(); 489 break; 490 case WifiMonitor.PNO_SCAN_RESULTS_EVENT: 491 pollLatestScanDataForPno(); 492 processPendingScans(); 493 break; 494 case WifiMonitor.SCAN_RESULTS_EVENT: 495 mAlarmManager.cancel(mScanTimeoutListener); 496 pollLatestScanData(); 497 processPendingScans(); 498 break; 499 default: 500 // ignore unknown event 501 } 502 return true; 503 } 504 505 private void reportScanFailure() { 506 synchronized (mSettingsLock) { 507 if (mLastScanSettings != null) { 508 if (mLastScanSettings.singleScanEventHandler != null) { 509 mLastScanSettings.singleScanEventHandler 510 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 511 } 512 // TODO(b/27769665) background scans should be failed too if scans fail enough 513 mLastScanSettings = null; 514 } 515 } 516 } 517 518 private void reportPnoScanFailure() { 519 synchronized (mSettingsLock) { 520 if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) { 521 if (mLastScanSettings.pnoScanEventHandler != null) { 522 mLastScanSettings.pnoScanEventHandler.onPnoScanFailed(); 523 } 524 // Clean up PNO state, we don't want to continue PNO scanning. 525 mPnoSettings = null; 526 mPnoEventHandler = null; 527 mLastScanSettings = null; 528 } 529 } 530 } 531 532 private void pollLatestScanDataForPno() { 533 synchronized (mSettingsLock) { 534 if (mLastScanSettings == null) { 535 // got a scan before we started scanning or after scan was canceled 536 return; 537 } 538 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 539 List<ScanResult> hwPnoScanResults = new ArrayList<>(); 540 int numFilteredScanResults = 0; 541 for (int i = 0; i < nativeResults.size(); ++i) { 542 ScanResult result = nativeResults.get(i).getScanResult(); 543 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 544 if (timestamp_ms > mLastScanSettings.startTime) { 545 if (mLastScanSettings.hwPnoScanActive) { 546 hwPnoScanResults.add(result); 547 } 548 } else { 549 numFilteredScanResults++; 550 } 551 } 552 553 if (numFilteredScanResults != 0) { 554 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results."); 555 } 556 557 if (mLastScanSettings.hwPnoScanActive 558 && mLastScanSettings.pnoScanEventHandler != null) { 559 ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()]; 560 for (int i = 0; i < pnoScanResultsArray.length; ++i) { 561 ScanResult result = nativeResults.get(i).getScanResult(); 562 pnoScanResultsArray[i] = hwPnoScanResults.get(i); 563 } 564 mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); 565 } 566 // On pno scan result event, we are expecting a mLastScanSettings for pno scan. 567 // However, if unlikey mLastScanSettings is for single scan, we need this part 568 // to protect from leaving WifiSingleScanStateMachine in a forever wait state. 569 if (mLastScanSettings.singleScanActive 570 && mLastScanSettings.singleScanEventHandler != null) { 571 Log.w(TAG, "Polling pno scan result when single scan is active, reporting" 572 + " single scan failure"); 573 mLastScanSettings.singleScanEventHandler 574 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 575 } 576 // mLastScanSettings is for either single/batched scan or pno scan. 577 // We can safely set it to null when pno scan finishes. 578 mLastScanSettings = null; 579 } 580 } 581 582 /** 583 * Check if the provided channel collection contains all the channels. 584 */ 585 private static boolean isAllChannelsScanned(ChannelCollection channelCollection) { 586 // TODO(b/62253332): Get rid of this hack. 587 // We're treating 2g + 5g and 2g + 5g + dfs as all channels scanned to work around 588 // the lack of a proper cache. 589 return (channelCollection.containsBand(WifiScanner.WIFI_BAND_24_GHZ) 590 && channelCollection.containsBand(WifiScanner.WIFI_BAND_5_GHZ)); 591 } 592 593 private void pollLatestScanData() { 594 synchronized (mSettingsLock) { 595 if (mLastScanSettings == null) { 596 // got a scan before we started scanning or after scan was canceled 597 return; 598 } 599 600 if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId); 601 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 602 List<ScanResult> singleScanResults = new ArrayList<>(); 603 List<ScanResult> backgroundScanResults = new ArrayList<>(); 604 int numFilteredScanResults = 0; 605 for (int i = 0; i < nativeResults.size(); ++i) { 606 ScanResult result = nativeResults.get(i).getScanResult(); 607 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 608 if (timestamp_ms > mLastScanSettings.startTime) { 609 if (mLastScanSettings.backgroundScanActive) { 610 backgroundScanResults.add(result); 611 } 612 if (mLastScanSettings.singleScanActive 613 && mLastScanSettings.singleScanFreqs.containsChannel( 614 result.frequency)) { 615 singleScanResults.add(result); 616 } 617 } else { 618 numFilteredScanResults++; 619 } 620 } 621 if (numFilteredScanResults != 0) { 622 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); 623 } 624 625 if (mLastScanSettings.backgroundScanActive) { 626 if (mBackgroundScanEventHandler != null) { 627 if ((mLastScanSettings.reportEvents 628 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 629 for (ScanResult scanResult : backgroundScanResults) { 630 // TODO(b/27506257): Fill in correct bucketsScanned value 631 mBackgroundScanEventHandler.onFullScanResult(scanResult, 0); 632 } 633 } 634 } 635 636 Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR); 637 ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps, 638 backgroundScanResults.size())]; 639 for (int i = 0; i < scanResultsArray.length; ++i) { 640 scanResultsArray[i] = backgroundScanResults.get(i); 641 } 642 643 if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 644 // TODO(b/27506257): Fill in correct bucketsScanned value 645 mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 646 scanResultsArray)); 647 } 648 649 if (mBackgroundScanEventHandler != null) { 650 if ((mLastScanSettings.reportEvents 651 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 652 || (mLastScanSettings.reportEvents 653 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0 654 || (mLastScanSettings.reportEvents 655 == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 656 && (mBackgroundScanBuffer.size() 657 >= (mBackgroundScanBuffer.capacity() 658 * mLastScanSettings.reportPercentThreshold 659 / 100) 660 || mBackgroundScanBuffer.size() 661 >= mLastScanSettings.reportNumScansThreshold))) { 662 mBackgroundScanEventHandler 663 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 664 } 665 } 666 } 667 668 if (mLastScanSettings.singleScanActive 669 && mLastScanSettings.singleScanEventHandler != null) { 670 if (mLastScanSettings.reportSingleScanFullResults) { 671 for (ScanResult scanResult : singleScanResults) { 672 // ignore buckets scanned since there is only one bucket for a single scan 673 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 674 /* bucketsScanned */ 0); 675 } 676 } 677 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 678 mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0, 679 isAllChannelsScanned(mLastScanSettings.singleScanFreqs), 680 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 681 mLastScanSettings.singleScanEventHandler 682 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 683 } 684 685 mLastScanSettings = null; 686 } 687 } 688 689 690 @Override 691 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 692 synchronized (mSettingsLock) { 693 WifiScanner.ScanData[] results = mBackgroundScanBuffer.get(); 694 if (flush) { 695 mBackgroundScanBuffer.clear(); 696 } 697 return results; 698 } 699 } 700 701 private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { 702 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 703 } 704 705 private void stopHwPnoScan() { 706 mHwPnoDebouncer.stopPnoScan(); 707 } 708 709 private void pauseHwPnoScan() { 710 mHwPnoDebouncer.forceStopPnoScan(); 711 } 712 713 private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) { 714 mHwPnoDebouncer.forceStopPnoScan(); 715 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 716 } 717 718 /** 719 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 720 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 721 * @return true if HW PNO scan is required, false otherwise. 722 */ 723 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 724 return (!isConnectedPno & mHwPnoScanSupported); 725 } 726 727 private boolean isHwPnoScanRequired() { 728 if (mPnoSettings == null) return false; 729 return isHwPnoScanRequired(mPnoSettings.isConnected); 730 } 731 732 @Override 733 public boolean setHwPnoList(WifiNative.PnoSettings settings, 734 WifiNative.PnoEventHandler eventHandler) { 735 synchronized (mSettingsLock) { 736 if (mPnoSettings != null) { 737 Log.w(TAG, "Already running a PNO scan"); 738 return false; 739 } 740 mPnoEventHandler = eventHandler; 741 mPnoSettings = settings; 742 743 // For wificond based PNO, we start the scan immediately when we set pno list. 744 processPendingScans(); 745 return true; 746 } 747 } 748 749 @Override 750 public boolean resetHwPnoList() { 751 synchronized (mSettingsLock) { 752 if (mPnoSettings == null) { 753 Log.w(TAG, "No PNO scan running"); 754 return false; 755 } 756 mPnoEventHandler = null; 757 mPnoSettings = null; 758 // For wificond based PNO, we stop the scan immediately when we reset pno list. 759 stopHwPnoScan(); 760 return true; 761 } 762 } 763 764 @Override 765 public boolean isHwPnoSupported(boolean isConnectedPno) { 766 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 767 return isHwPnoScanRequired(isConnectedPno); 768 } 769 770 @Override 771 public boolean shouldScheduleBackgroundScanForHwPno() { 772 return false; 773 } 774 775 private static class LastScanSettings { 776 public long startTime; 777 778 LastScanSettings(long startTime) { 779 this.startTime = startTime; 780 } 781 782 // Background settings 783 public boolean backgroundScanActive = false; 784 public int scanId; 785 public int maxAps; 786 public int reportEvents; 787 public int reportNumScansThreshold; 788 public int reportPercentThreshold; 789 790 public void setBackgroundScan(int scanId, int maxAps, int reportEvents, 791 int reportNumScansThreshold, int reportPercentThreshold) { 792 this.backgroundScanActive = true; 793 this.scanId = scanId; 794 this.maxAps = maxAps; 795 this.reportEvents = reportEvents; 796 this.reportNumScansThreshold = reportNumScansThreshold; 797 this.reportPercentThreshold = reportPercentThreshold; 798 } 799 800 // Single scan settings 801 public boolean singleScanActive = false; 802 public boolean reportSingleScanFullResults; 803 public ChannelCollection singleScanFreqs; 804 public WifiNative.ScanEventHandler singleScanEventHandler; 805 806 public void setSingleScan(boolean reportSingleScanFullResults, 807 ChannelCollection singleScanFreqs, 808 WifiNative.ScanEventHandler singleScanEventHandler) { 809 singleScanActive = true; 810 this.reportSingleScanFullResults = reportSingleScanFullResults; 811 this.singleScanFreqs = singleScanFreqs; 812 this.singleScanEventHandler = singleScanEventHandler; 813 } 814 815 public boolean hwPnoScanActive = false; 816 public WifiNative.PnoNetwork[] pnoNetworkList; 817 public WifiNative.PnoEventHandler pnoScanEventHandler; 818 819 public void setHwPnoScan( 820 WifiNative.PnoNetwork[] pnoNetworkList, 821 WifiNative.PnoEventHandler pnoScanEventHandler) { 822 hwPnoScanActive = true; 823 this.pnoNetworkList = pnoNetworkList; 824 this.pnoScanEventHandler = pnoScanEventHandler; 825 } 826 } 827 828 829 private static class ScanBuffer { 830 private final ArrayDeque<WifiScanner.ScanData> mBuffer; 831 private int mCapacity; 832 833 ScanBuffer(int capacity) { 834 mCapacity = capacity; 835 mBuffer = new ArrayDeque<>(mCapacity); 836 } 837 838 public int size() { 839 return mBuffer.size(); 840 } 841 842 public int capacity() { 843 return mCapacity; 844 } 845 846 public boolean isFull() { 847 return size() == mCapacity; 848 } 849 850 public void add(WifiScanner.ScanData scanData) { 851 if (isFull()) { 852 mBuffer.pollFirst(); 853 } 854 mBuffer.offerLast(scanData); 855 } 856 857 public void clear() { 858 mBuffer.clear(); 859 } 860 861 public WifiScanner.ScanData[] get() { 862 return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]); 863 } 864 } 865 866 /** 867 * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO 868 * state too often which is not handled very well by some drivers. 869 * Note: This is not thread safe! 870 */ 871 public static class HwPnoDebouncer { 872 public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor"; 873 private static final int MINIMUM_PNO_GAP_MS = 5 * 1000; 874 875 private final WifiNative mWifiNative; 876 private final AlarmManager mAlarmManager; 877 private final Handler mEventHandler; 878 private final Clock mClock; 879 private long mLastPnoChangeTimeStamp = -1L; 880 private boolean mExpectedPnoState = false; 881 private boolean mCurrentPnoState = false;; 882 private boolean mWaitForTimer = false; 883 private Listener mListener; 884 private WifiNative.PnoSettings mPnoSettings; 885 886 /** 887 * Interface used to indicate PNO scan notifications. 888 */ 889 public interface Listener { 890 /** 891 * Used to indicate a delayed PNO scan request failure. 892 */ 893 void onPnoScanFailed(); 894 } 895 896 public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager, 897 Handler eventHandler, Clock clock) { 898 mWifiNative = wifiNative; 899 mAlarmManager = alarmManager; 900 mEventHandler = eventHandler; 901 mClock = clock; 902 } 903 904 /** 905 * Enable PNO state in wificond 906 */ 907 private boolean startPnoScanInternal() { 908 if (mCurrentPnoState) { 909 if (DBG) Log.d(TAG, "PNO state is already enable"); 910 return true; 911 } 912 if (mPnoSettings == null) { 913 Log.e(TAG, "PNO state change to enable failed, no available Pno settings"); 914 return false; 915 } 916 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 917 Log.d(TAG, "Remove all networks from supplicant before starting PNO scan"); 918 mWifiNative.removeAllNetworks(); 919 if (mWifiNative.startPnoScan(mPnoSettings)) { 920 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable"); 921 mCurrentPnoState = true; 922 return true; 923 } else { 924 Log.e(TAG, "PNO state change to enable failed"); 925 mCurrentPnoState = false; 926 } 927 return false; 928 } 929 930 /** 931 * Disable PNO state in wificond 932 */ 933 private boolean stopPnoScanInternal() { 934 if (!mCurrentPnoState) { 935 if (DBG) Log.d(TAG, "PNO state is already disable"); 936 return true; 937 } 938 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 939 if (mWifiNative.stopPnoScan()) { 940 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable"); 941 mCurrentPnoState = false; 942 return true; 943 } else { 944 Log.e(TAG, "PNO state change to disable failed"); 945 mCurrentPnoState = false; 946 } 947 return false; 948 } 949 950 private final AlarmManager.OnAlarmListener mAlarmListener = 951 new AlarmManager.OnAlarmListener() { 952 public void onAlarm() { 953 if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState); 954 if (mExpectedPnoState) { 955 if (!startPnoScanInternal()) { 956 if (mListener != null) { 957 mListener.onPnoScanFailed(); 958 } 959 } 960 } else { 961 stopPnoScanInternal(); 962 } 963 mWaitForTimer = false; 964 } 965 }; 966 967 /** 968 * Enable/Disable PNO state. This method will debounce PNO scan requests. 969 * @param enable boolean indicating whether PNO is being enabled or disabled. 970 */ 971 private boolean setPnoState(boolean enable) { 972 boolean isSuccess = true; 973 mExpectedPnoState = enable; 974 if (!mWaitForTimer) { 975 long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp; 976 if (timeDifference >= MINIMUM_PNO_GAP_MS) { 977 if (enable) { 978 isSuccess = startPnoScanInternal(); 979 } else { 980 isSuccess = stopPnoScanInternal(); 981 } 982 } else { 983 long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference; 984 Log.d(TAG, "Start PNO timer with delay " + alarmTimeout); 985 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 986 mClock.getElapsedSinceBootMillis() + alarmTimeout, 987 PNO_DEBOUNCER_ALARM_TAG, 988 mAlarmListener, mEventHandler); 989 mWaitForTimer = true; 990 } 991 } 992 return isSuccess; 993 } 994 995 /** 996 * Start PNO scan 997 */ 998 public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) { 999 if (DBG) Log.d(TAG, "Starting PNO scan"); 1000 mListener = listener; 1001 mPnoSettings = pnoSettings; 1002 if (!setPnoState(true)) { 1003 mListener = null; 1004 return false; 1005 } 1006 return true; 1007 } 1008 1009 /** 1010 * Stop PNO scan 1011 */ 1012 public void stopPnoScan() { 1013 if (DBG) Log.d(TAG, "Stopping PNO scan"); 1014 setPnoState(false); 1015 mListener = null; 1016 } 1017 1018 /** 1019 * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO 1020 * scan immediately. 1021 */ 1022 public void forceStopPnoScan() { 1023 if (DBG) Log.d(TAG, "Force stopping Pno scan"); 1024 // Cancel the debounce timer and stop PNO scan. 1025 if (mWaitForTimer) { 1026 mAlarmManager.cancel(mAlarmListener); 1027 mWaitForTimer = false; 1028 } 1029 stopPnoScanInternal(); 1030 } 1031 } 1032 } 1033