WificondScannerImpl.java revision 9dc9a8750ecd1ab25c5b4c7d17c8930ca2ffb6c3
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 17package com.android.server.wifi.scanner; 18 19import android.app.AlarmManager; 20import android.content.Context; 21import android.net.wifi.ScanResult; 22import android.net.wifi.WifiScanner; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.util.Log; 27 28import com.android.internal.R; 29import com.android.server.wifi.Clock; 30import com.android.server.wifi.ScanDetail; 31import com.android.server.wifi.WifiMonitor; 32import com.android.server.wifi.WifiNative; 33import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; 34 35import java.util.ArrayDeque; 36import java.util.ArrayList; 37import java.util.Arrays; 38import java.util.Collections; 39import java.util.HashSet; 40import java.util.List; 41import 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 */ 47public 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 // mLastScanSettings is for either single/batched scan or pno scan. 567 // We can safely set it to null when pno scan finishes. 568 mLastScanSettings = null; 569 } 570 } 571 572 private void pollLatestScanData() { 573 synchronized (mSettingsLock) { 574 if (mLastScanSettings == null) { 575 // got a scan before we started scanning or after scan was canceled 576 return; 577 } 578 579 if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId); 580 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 581 List<ScanResult> singleScanResults = new ArrayList<>(); 582 List<ScanResult> backgroundScanResults = new ArrayList<>(); 583 int numFilteredScanResults = 0; 584 for (int i = 0; i < nativeResults.size(); ++i) { 585 ScanResult result = nativeResults.get(i).getScanResult(); 586 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 587 if (timestamp_ms > mLastScanSettings.startTime) { 588 if (mLastScanSettings.backgroundScanActive) { 589 backgroundScanResults.add(result); 590 } 591 if (mLastScanSettings.singleScanActive 592 && mLastScanSettings.singleScanFreqs.containsChannel( 593 result.frequency)) { 594 singleScanResults.add(result); 595 } 596 } else { 597 numFilteredScanResults++; 598 } 599 } 600 if (numFilteredScanResults != 0) { 601 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); 602 } 603 604 if (mLastScanSettings.backgroundScanActive) { 605 if (mBackgroundScanEventHandler != null) { 606 if ((mLastScanSettings.reportEvents 607 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 608 for (ScanResult scanResult : backgroundScanResults) { 609 // TODO(b/27506257): Fill in correct bucketsScanned value 610 mBackgroundScanEventHandler.onFullScanResult(scanResult, 0); 611 } 612 } 613 } 614 615 Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR); 616 ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps, 617 backgroundScanResults.size())]; 618 for (int i = 0; i < scanResultsArray.length; ++i) { 619 scanResultsArray[i] = backgroundScanResults.get(i); 620 } 621 622 if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 623 // TODO(b/27506257): Fill in correct bucketsScanned value 624 mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 625 scanResultsArray)); 626 } 627 628 if (mBackgroundScanEventHandler != null) { 629 if ((mLastScanSettings.reportEvents 630 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 631 || (mLastScanSettings.reportEvents 632 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0 633 || (mLastScanSettings.reportEvents 634 == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 635 && (mBackgroundScanBuffer.size() 636 >= (mBackgroundScanBuffer.capacity() 637 * mLastScanSettings.reportPercentThreshold 638 / 100) 639 || mBackgroundScanBuffer.size() 640 >= mLastScanSettings.reportNumScansThreshold))) { 641 mBackgroundScanEventHandler 642 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 643 } 644 } 645 } 646 647 if (mLastScanSettings.singleScanActive 648 && mLastScanSettings.singleScanEventHandler != null) { 649 if (mLastScanSettings.reportSingleScanFullResults) { 650 for (ScanResult scanResult : singleScanResults) { 651 // ignore buckets scanned since there is only one bucket for a single scan 652 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 653 /* bucketsScanned */ 0); 654 } 655 } 656 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 657 mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0, 658 mLastScanSettings.singleScanFreqs.isAllChannels(), 659 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 660 mLastScanSettings.singleScanEventHandler 661 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 662 } 663 664 mLastScanSettings = null; 665 } 666 } 667 668 669 @Override 670 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 671 synchronized (mSettingsLock) { 672 WifiScanner.ScanData[] results = mBackgroundScanBuffer.get(); 673 if (flush) { 674 mBackgroundScanBuffer.clear(); 675 } 676 return results; 677 } 678 } 679 680 private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { 681 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 682 } 683 684 private void stopHwPnoScan() { 685 mHwPnoDebouncer.stopPnoScan(); 686 } 687 688 private void pauseHwPnoScan() { 689 mHwPnoDebouncer.forceStopPnoScan(); 690 } 691 692 private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) { 693 mHwPnoDebouncer.forceStopPnoScan(); 694 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 695 } 696 697 /** 698 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 699 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 700 * @return true if HW PNO scan is required, false otherwise. 701 */ 702 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 703 return (!isConnectedPno & mHwPnoScanSupported); 704 } 705 706 private boolean isHwPnoScanRequired() { 707 if (mPnoSettings == null) return false; 708 return isHwPnoScanRequired(mPnoSettings.isConnected); 709 } 710 711 @Override 712 public boolean setHwPnoList(WifiNative.PnoSettings settings, 713 WifiNative.PnoEventHandler eventHandler) { 714 synchronized (mSettingsLock) { 715 if (mPnoSettings != null) { 716 Log.w(TAG, "Already running a PNO scan"); 717 return false; 718 } 719 mPnoEventHandler = eventHandler; 720 mPnoSettings = settings; 721 722 // For wificond based PNO, we start the scan immediately when we set pno list. 723 processPendingScans(); 724 return true; 725 } 726 } 727 728 @Override 729 public boolean resetHwPnoList() { 730 synchronized (mSettingsLock) { 731 if (mPnoSettings == null) { 732 Log.w(TAG, "No PNO scan running"); 733 return false; 734 } 735 mPnoEventHandler = null; 736 mPnoSettings = null; 737 // For wificond based PNO, we stop the scan immediately when we reset pno list. 738 stopHwPnoScan(); 739 return true; 740 } 741 } 742 743 @Override 744 public boolean isHwPnoSupported(boolean isConnectedPno) { 745 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 746 return isHwPnoScanRequired(isConnectedPno); 747 } 748 749 @Override 750 public boolean shouldScheduleBackgroundScanForHwPno() { 751 return false; 752 } 753 754 private static class LastScanSettings { 755 public long startTime; 756 757 LastScanSettings(long startTime) { 758 this.startTime = startTime; 759 } 760 761 // Background settings 762 public boolean backgroundScanActive = false; 763 public int scanId; 764 public int maxAps; 765 public int reportEvents; 766 public int reportNumScansThreshold; 767 public int reportPercentThreshold; 768 769 public void setBackgroundScan(int scanId, int maxAps, int reportEvents, 770 int reportNumScansThreshold, int reportPercentThreshold) { 771 this.backgroundScanActive = true; 772 this.scanId = scanId; 773 this.maxAps = maxAps; 774 this.reportEvents = reportEvents; 775 this.reportNumScansThreshold = reportNumScansThreshold; 776 this.reportPercentThreshold = reportPercentThreshold; 777 } 778 779 // Single scan settings 780 public boolean singleScanActive = false; 781 public boolean reportSingleScanFullResults; 782 public ChannelCollection singleScanFreqs; 783 public WifiNative.ScanEventHandler singleScanEventHandler; 784 785 public void setSingleScan(boolean reportSingleScanFullResults, 786 ChannelCollection singleScanFreqs, 787 WifiNative.ScanEventHandler singleScanEventHandler) { 788 singleScanActive = true; 789 this.reportSingleScanFullResults = reportSingleScanFullResults; 790 this.singleScanFreqs = singleScanFreqs; 791 this.singleScanEventHandler = singleScanEventHandler; 792 } 793 794 public boolean hwPnoScanActive = false; 795 public WifiNative.PnoNetwork[] pnoNetworkList; 796 public WifiNative.PnoEventHandler pnoScanEventHandler; 797 798 public void setHwPnoScan( 799 WifiNative.PnoNetwork[] pnoNetworkList, 800 WifiNative.PnoEventHandler pnoScanEventHandler) { 801 hwPnoScanActive = true; 802 this.pnoNetworkList = pnoNetworkList; 803 this.pnoScanEventHandler = pnoScanEventHandler; 804 } 805 } 806 807 808 private static class ScanBuffer { 809 private final ArrayDeque<WifiScanner.ScanData> mBuffer; 810 private int mCapacity; 811 812 ScanBuffer(int capacity) { 813 mCapacity = capacity; 814 mBuffer = new ArrayDeque<>(mCapacity); 815 } 816 817 public int size() { 818 return mBuffer.size(); 819 } 820 821 public int capacity() { 822 return mCapacity; 823 } 824 825 public boolean isFull() { 826 return size() == mCapacity; 827 } 828 829 public void add(WifiScanner.ScanData scanData) { 830 if (isFull()) { 831 mBuffer.pollFirst(); 832 } 833 mBuffer.offerLast(scanData); 834 } 835 836 public void clear() { 837 mBuffer.clear(); 838 } 839 840 public WifiScanner.ScanData[] get() { 841 return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]); 842 } 843 } 844 845 /** 846 * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO 847 * state too often which is not handled very well by some drivers. 848 * Note: This is not thread safe! 849 */ 850 public static class HwPnoDebouncer { 851 public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor"; 852 private static final int MINIMUM_PNO_GAP_MS = 5 * 1000; 853 854 private final WifiNative mWifiNative; 855 private final AlarmManager mAlarmManager; 856 private final Handler mEventHandler; 857 private final Clock mClock; 858 private long mLastPnoChangeTimeStamp = -1L; 859 private boolean mExpectedPnoState = false; 860 private boolean mCurrentPnoState = false;; 861 private boolean mWaitForTimer = false; 862 private Listener mListener; 863 private WifiNative.PnoSettings mPnoSettings; 864 865 /** 866 * Interface used to indicate PNO scan notifications. 867 */ 868 public interface Listener { 869 /** 870 * Used to indicate a delayed PNO scan request failure. 871 */ 872 void onPnoScanFailed(); 873 } 874 875 public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager, 876 Handler eventHandler, Clock clock) { 877 mWifiNative = wifiNative; 878 mAlarmManager = alarmManager; 879 mEventHandler = eventHandler; 880 mClock = clock; 881 } 882 883 /** 884 * Enable PNO state in wificond 885 */ 886 private boolean startPnoScanInternal() { 887 if (mCurrentPnoState) { 888 if (DBG) Log.d(TAG, "PNO state is already enable"); 889 return true; 890 } 891 if (mPnoSettings == null) { 892 Log.e(TAG, "PNO state change to enable failed, no available Pno settings"); 893 return false; 894 } 895 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 896 Log.d(TAG, "Remove all networks from supplicant before starting PNO scan"); 897 mWifiNative.removeAllNetworks(); 898 if (mWifiNative.startPnoScan(mPnoSettings)) { 899 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable"); 900 mCurrentPnoState = true; 901 return true; 902 } else { 903 Log.e(TAG, "PNO state change to enable failed"); 904 mCurrentPnoState = false; 905 } 906 return false; 907 } 908 909 /** 910 * Disable PNO state in wificond 911 */ 912 private boolean stopPnoScanInternal() { 913 if (!mCurrentPnoState) { 914 if (DBG) Log.d(TAG, "PNO state is already disable"); 915 return true; 916 } 917 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 918 if (mWifiNative.stopPnoScan()) { 919 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable"); 920 mCurrentPnoState = false; 921 return true; 922 } else { 923 Log.e(TAG, "PNO state change to disable failed"); 924 mCurrentPnoState = false; 925 } 926 return false; 927 } 928 929 private final AlarmManager.OnAlarmListener mAlarmListener = 930 new AlarmManager.OnAlarmListener() { 931 public void onAlarm() { 932 if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState); 933 if (mExpectedPnoState) { 934 if (!startPnoScanInternal()) { 935 if (mListener != null) { 936 mListener.onPnoScanFailed(); 937 } 938 } 939 } else { 940 stopPnoScanInternal(); 941 } 942 mWaitForTimer = false; 943 } 944 }; 945 946 /** 947 * Enable/Disable PNO state. This method will debounce PNO scan requests. 948 * @param enable boolean indicating whether PNO is being enabled or disabled. 949 */ 950 private boolean setPnoState(boolean enable) { 951 boolean isSuccess = true; 952 mExpectedPnoState = enable; 953 if (!mWaitForTimer) { 954 long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp; 955 if (timeDifference >= MINIMUM_PNO_GAP_MS) { 956 if (enable) { 957 isSuccess = startPnoScanInternal(); 958 } else { 959 isSuccess = stopPnoScanInternal(); 960 } 961 } else { 962 long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference; 963 Log.d(TAG, "Start PNO timer with delay " + alarmTimeout); 964 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 965 mClock.getElapsedSinceBootMillis() + alarmTimeout, 966 PNO_DEBOUNCER_ALARM_TAG, 967 mAlarmListener, mEventHandler); 968 mWaitForTimer = true; 969 } 970 } 971 return isSuccess; 972 } 973 974 /** 975 * Start PNO scan 976 */ 977 public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) { 978 if (DBG) Log.d(TAG, "Starting PNO scan"); 979 mListener = listener; 980 mPnoSettings = pnoSettings; 981 if (!setPnoState(true)) { 982 mListener = null; 983 return false; 984 } 985 return true; 986 } 987 988 /** 989 * Stop PNO scan 990 */ 991 public void stopPnoScan() { 992 if (DBG) Log.d(TAG, "Stopping PNO scan"); 993 setPnoState(false); 994 mListener = null; 995 } 996 997 /** 998 * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO 999 * scan immediately. 1000 */ 1001 public void forceStopPnoScan() { 1002 if (DBG) Log.d(TAG, "Force stopping Pno scan"); 1003 // Cancel the debounce timer and stop PNO scan. 1004 if (mWaitForTimer) { 1005 mAlarmManager.cancel(mAlarmListener); 1006 mWaitForTimer = false; 1007 } 1008 stopPnoScanInternal(); 1009 } 1010 } 1011} 1012