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