WificondScannerImpl.java revision 8149a50728c1394e11a4441a1d76eb6d965a9097
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 figure out how to get channel information from supplicant 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 // TODO(b/36276738): Remove this after we fix b/36231150. 198 Log.d(TAG, "processPendingScans in request of startSingleScan"); 199 processPendingScans(); 200 return true; 201 } 202 } 203 204 @Override 205 public WifiScanner.ScanData getLatestSingleScanResults() { 206 return mLatestSingleScanResult; 207 } 208 209 @Override 210 public boolean startBatchedScan(WifiNative.ScanSettings settings, 211 WifiNative.ScanEventHandler eventHandler) { 212 if (settings == null || eventHandler == null) { 213 Log.w(TAG, "Invalid arguments for startBatched: settings=" + settings 214 + ",eventHandler=" + eventHandler); 215 return false; 216 } 217 218 if (settings.max_ap_per_scan < 0 || settings.max_ap_per_scan > MAX_APS_PER_SCAN) { 219 return false; 220 } 221 if (settings.num_buckets < 0 || settings.num_buckets > MAX_SCAN_BUCKETS) { 222 return false; 223 } 224 if (settings.report_threshold_num_scans < 0 225 || settings.report_threshold_num_scans > SCAN_BUFFER_CAPACITY) { 226 return false; 227 } 228 if (settings.report_threshold_percent < 0 || settings.report_threshold_percent > 100) { 229 return false; 230 } 231 if (settings.base_period_ms <= 0) { 232 return false; 233 } 234 for (int i = 0; i < settings.num_buckets; ++i) { 235 WifiNative.BucketSettings bucket = settings.buckets[i]; 236 if (bucket.period_ms % settings.base_period_ms != 0) { 237 return false; 238 } 239 } 240 241 synchronized (mSettingsLock) { 242 stopBatchedScan(); 243 if (DBG) { 244 Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period=" 245 + settings.base_period_ms + " ms"); 246 } 247 mPendingBackgroundScanSettings = settings; 248 mPendingBackgroundScanEventHandler = eventHandler; 249 handleScanPeriod(); // Try to start scan immediately 250 return true; 251 } 252 } 253 254 @Override 255 public void stopBatchedScan() { 256 synchronized (mSettingsLock) { 257 if (DBG) Log.d(TAG, "Stopping scan"); 258 mBackgroundScanSettings = null; 259 mBackgroundScanEventHandler = null; 260 mPendingBackgroundScanSettings = null; 261 mPendingBackgroundScanEventHandler = null; 262 mBackgroundScanPaused = false; 263 mBackgroundScanPeriodPending = false; 264 unscheduleScansLocked(); 265 } 266 // TODO(b/36276738): Remove this after we fix b/36231150. 267 Log.d(TAG, "processPendingScans in request of stopBatchedScan"); 268 processPendingScans(); 269 } 270 271 @Override 272 public void pauseBatchedScan() { 273 synchronized (mSettingsLock) { 274 if (DBG) Log.d(TAG, "Pausing scan"); 275 // if there isn't a pending scan then make the current scan pending 276 if (mPendingBackgroundScanSettings == null) { 277 mPendingBackgroundScanSettings = mBackgroundScanSettings; 278 mPendingBackgroundScanEventHandler = mBackgroundScanEventHandler; 279 } 280 mBackgroundScanSettings = null; 281 mBackgroundScanEventHandler = null; 282 mBackgroundScanPeriodPending = false; 283 mBackgroundScanPaused = true; 284 285 unscheduleScansLocked(); 286 287 WifiScanner.ScanData[] results = getLatestBatchedScanResults(/* flush = */ true); 288 if (mPendingBackgroundScanEventHandler != null) { 289 mPendingBackgroundScanEventHandler.onScanPaused(results); 290 } 291 } 292 // TODO(b/36276738): Remove this after we fix b/36231150. 293 Log.d(TAG, "processPendingScans in request of pauseBatchedScan"); 294 processPendingScans(); 295 } 296 297 @Override 298 public void restartBatchedScan() { 299 synchronized (mSettingsLock) { 300 if (DBG) Log.d(TAG, "Restarting scan"); 301 if (mPendingBackgroundScanEventHandler != null) { 302 mPendingBackgroundScanEventHandler.onScanRestarted(); 303 } 304 mBackgroundScanPaused = false; 305 handleScanPeriod(); 306 } 307 } 308 309 private void unscheduleScansLocked() { 310 mAlarmManager.cancel(mScanPeriodListener); 311 if (mLastScanSettings != null) { 312 mLastScanSettings.backgroundScanActive = false; 313 } 314 } 315 316 private void handleScanPeriod() { 317 synchronized (mSettingsLock) { 318 mBackgroundScanPeriodPending = true; 319 // TODO(b/36276738): Remove this after we fix b/36231150. 320 Log.d(TAG, "processPendingScans in request of handleScanPeriod"); 321 processPendingScans(); 322 } 323 } 324 325 private void handleScanTimeout() { 326 Log.e(TAG, "Timed out waiting for scan result from wificond"); 327 reportScanFailure(); 328 // TODO(b/36276738): Remove this after we fix b/36231150. 329 Log.d(TAG, "processPendingScans in request of handleScanTimeout"); 330 processPendingScans(); 331 } 332 333 private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) { 334 return (mLastScanSettings == null || !Arrays.equals( 335 newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList)); 336 } 337 338 private void processPendingScans() { 339 synchronized (mSettingsLock) { 340 // Wait for the active scan result to come back to reschedule other scans, 341 // unless if HW pno scan is running. Hw PNO scans are paused it if there 342 // are other pending scans, 343 if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) { 344 return; 345 } 346 347 ChannelCollection allFreqs = mChannelHelper.createChannelCollection(); 348 Set<String> hiddenNetworkSSIDSet = new HashSet<>(); 349 final LastScanSettings newScanSettings = 350 new LastScanSettings(mClock.getElapsedSinceBootMillis()); 351 352 // Update scan settings if there is a pending scan 353 if (!mBackgroundScanPaused) { 354 if (mPendingBackgroundScanSettings != null) { 355 mBackgroundScanSettings = mPendingBackgroundScanSettings; 356 mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler; 357 mNextBackgroundScanPeriod = 0; 358 mPendingBackgroundScanSettings = null; 359 mPendingBackgroundScanEventHandler = null; 360 mBackgroundScanPeriodPending = true; 361 } 362 if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) { 363 int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch 364 for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets; 365 ++bucket_id) { 366 WifiNative.BucketSettings bucket = 367 mBackgroundScanSettings.buckets[bucket_id]; 368 if (mNextBackgroundScanPeriod % (bucket.period_ms 369 / mBackgroundScanSettings.base_period_ms) == 0) { 370 if ((bucket.report_events 371 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { 372 reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 373 } 374 if ((bucket.report_events 375 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 376 reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 377 } 378 // only no batch if all buckets specify it 379 if ((bucket.report_events 380 & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 381 reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; 382 } 383 384 allFreqs.addChannels(bucket); 385 } 386 } 387 if (!allFreqs.isEmpty()) { 388 newScanSettings.setBackgroundScan(mNextBackgroundScanId++, 389 mBackgroundScanSettings.max_ap_per_scan, reportEvents, 390 mBackgroundScanSettings.report_threshold_num_scans, 391 mBackgroundScanSettings.report_threshold_percent); 392 } 393 mNextBackgroundScanPeriod++; 394 mBackgroundScanPeriodPending = false; 395 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 396 mClock.getElapsedSinceBootMillis() 397 + mBackgroundScanSettings.base_period_ms, 398 BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler); 399 } 400 } 401 402 if (mPendingSingleScanSettings != null) { 403 boolean reportFullResults = false; 404 ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection(); 405 for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) { 406 WifiNative.BucketSettings bucketSettings = 407 mPendingSingleScanSettings.buckets[i]; 408 if ((bucketSettings.report_events 409 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 410 reportFullResults = true; 411 } 412 singleScanFreqs.addChannels(bucketSettings); 413 allFreqs.addChannels(bucketSettings); 414 } 415 newScanSettings.setSingleScan(reportFullResults, singleScanFreqs, 416 mPendingSingleScanEventHandler); 417 418 WifiNative.HiddenNetwork[] hiddenNetworks = 419 mPendingSingleScanSettings.hiddenNetworks; 420 if (hiddenNetworks != null) { 421 int numHiddenNetworks = 422 Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN); 423 for (int i = 0; i < numHiddenNetworks; i++) { 424 hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid); 425 } 426 } 427 428 mPendingSingleScanSettings = null; 429 mPendingSingleScanEventHandler = null; 430 } 431 432 if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) 433 && !allFreqs.isEmpty()) { 434 pauseHwPnoScan(); 435 Set<Integer> freqs = allFreqs.getSupplicantScanFreqs(); 436 boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); 437 if (success) { 438 // TODO handle scan timeout 439 // TODO(b/36276738): Change this to DBG log after we fix b/36231150. 440 Log.d(TAG, "Starting wifi scan for freqs=" + freqs 441 + ", background=" + newScanSettings.backgroundScanActive 442 + ", single=" + newScanSettings.singleScanActive); 443 mLastScanSettings = newScanSettings; 444 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 445 mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, 446 TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); 447 } else { 448 Log.e(TAG, "Failed to start scan, freqs=" + freqs); 449 // indicate scan failure async 450 mEventHandler.post(new Runnable() { 451 public void run() { 452 if (newScanSettings.singleScanEventHandler != null) { 453 newScanSettings.singleScanEventHandler 454 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 455 } 456 } 457 }); 458 // TODO(b/27769665) background scans should be failed too if scans fail enough 459 } 460 } else if (isHwPnoScanRequired()) { 461 newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler); 462 boolean status; 463 // If the PNO network list has changed from the previous request, ensure that 464 // we bypass the debounce logic and restart PNO scan. 465 if (isDifferentPnoScanSettings(newScanSettings)) { 466 status = restartHwPnoScan(mPnoSettings); 467 } else { 468 status = startHwPnoScan(mPnoSettings); 469 } 470 if (status) { 471 mLastScanSettings = newScanSettings; 472 } else { 473 Log.e(TAG, "Failed to start PNO scan"); 474 // indicate scan failure async 475 mEventHandler.post(new Runnable() { 476 public void run() { 477 if (mPnoEventHandler != null) { 478 mPnoEventHandler.onPnoScanFailed(); 479 } 480 // Clean up PNO state, we don't want to continue PNO scanning. 481 mPnoSettings = null; 482 mPnoEventHandler = null; 483 } 484 }); 485 } 486 } 487 } 488 } 489 490 @Override 491 public boolean handleMessage(Message msg) { 492 switch(msg.what) { 493 case WifiMonitor.SCAN_FAILED_EVENT: 494 Log.w(TAG, "Scan failed"); 495 mAlarmManager.cancel(mScanTimeoutListener); 496 reportScanFailure(); 497 processPendingScans(); 498 break; 499 case WifiMonitor.PNO_SCAN_RESULTS_EVENT: 500 mAlarmManager.cancel(mScanTimeoutListener); 501 pollLatestScanDataForPno(); 502 // TODO(b/36276738): Remove this after we fix b/36231150. 503 Log.d(TAG, "processPendingScans in request of SCHED_SCAN_RESULTS_EVENT"); 504 processPendingScans(); 505 break; 506 case WifiMonitor.SCAN_RESULTS_EVENT: 507 mAlarmManager.cancel(mScanTimeoutListener); 508 pollLatestScanData(); 509 // TODO(b/36276738): Remove this after we fix b/36231150. 510 Log.d(TAG, "processPendingScans in request of SCAN_RESULTS_EVENT"); 511 processPendingScans(); 512 break; 513 default: 514 // ignore unknown event 515 } 516 return true; 517 } 518 519 private void reportScanFailure() { 520 synchronized (mSettingsLock) { 521 if (mLastScanSettings != null) { 522 if (mLastScanSettings.singleScanEventHandler != null) { 523 mLastScanSettings.singleScanEventHandler 524 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 525 } 526 // TODO(b/27769665) background scans should be failed too if scans fail enough 527 mLastScanSettings = null; 528 } 529 } 530 } 531 532 private void reportPnoScanFailure() { 533 synchronized (mSettingsLock) { 534 if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) { 535 if (mLastScanSettings.pnoScanEventHandler != null) { 536 mLastScanSettings.pnoScanEventHandler.onPnoScanFailed(); 537 } 538 // Clean up PNO state, we don't want to continue PNO scanning. 539 mPnoSettings = null; 540 mPnoEventHandler = null; 541 mLastScanSettings = null; 542 } 543 } 544 } 545 546 private void pollLatestScanDataForPno() { 547 synchronized (mSettingsLock) { 548 if (mLastScanSettings == null) { 549 // got a scan before we started scanning or after scan was canceled 550 return; 551 } 552 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 553 List<ScanResult> hwPnoScanResults = new ArrayList<>(); 554 int numFilteredScanResults = 0; 555 for (int i = 0; i < nativeResults.size(); ++i) { 556 ScanResult result = nativeResults.get(i).getScanResult(); 557 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 558 if (timestamp_ms > mLastScanSettings.startTime) { 559 if (mLastScanSettings.hwPnoScanActive) { 560 hwPnoScanResults.add(result); 561 } 562 } else { 563 numFilteredScanResults++; 564 } 565 } 566 567 if (numFilteredScanResults != 0) { 568 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results."); 569 } 570 571 if (mLastScanSettings.hwPnoScanActive 572 && mLastScanSettings.pnoScanEventHandler != null) { 573 ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()]; 574 for (int i = 0; i < pnoScanResultsArray.length; ++i) { 575 ScanResult result = nativeResults.get(i).getScanResult(); 576 pnoScanResultsArray[i] = hwPnoScanResults.get(i); 577 } 578 mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); 579 } 580 // mLastScanSettings is for either single/batched scan or pno scan. 581 // We can safely set it to null when pno scan finishes. 582 mLastScanSettings = null; 583 } 584 } 585 586 private void pollLatestScanData() { 587 synchronized (mSettingsLock) { 588 if (mLastScanSettings == null) { 589 // got a scan before we started scanning or after scan was canceled 590 return; 591 } 592 593 if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId); 594 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 595 List<ScanResult> singleScanResults = new ArrayList<>(); 596 List<ScanResult> backgroundScanResults = new ArrayList<>(); 597 int numFilteredScanResults = 0; 598 for (int i = 0; i < nativeResults.size(); ++i) { 599 ScanResult result = nativeResults.get(i).getScanResult(); 600 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 601 if (timestamp_ms > mLastScanSettings.startTime) { 602 if (mLastScanSettings.backgroundScanActive) { 603 backgroundScanResults.add(result); 604 } 605 if (mLastScanSettings.singleScanActive 606 && mLastScanSettings.singleScanFreqs.containsChannel( 607 result.frequency)) { 608 singleScanResults.add(result); 609 } 610 } else { 611 numFilteredScanResults++; 612 } 613 } 614 if (numFilteredScanResults != 0) { 615 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); 616 } 617 618 if (mLastScanSettings.backgroundScanActive) { 619 if (mBackgroundScanEventHandler != null) { 620 if ((mLastScanSettings.reportEvents 621 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 622 for (ScanResult scanResult : backgroundScanResults) { 623 // TODO(b/27506257): Fill in correct bucketsScanned value 624 mBackgroundScanEventHandler.onFullScanResult(scanResult, 0); 625 } 626 } 627 } 628 629 Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR); 630 ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps, 631 backgroundScanResults.size())]; 632 for (int i = 0; i < scanResultsArray.length; ++i) { 633 scanResultsArray[i] = backgroundScanResults.get(i); 634 } 635 636 if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 637 // TODO(b/27506257): Fill in correct bucketsScanned value 638 mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 639 scanResultsArray)); 640 } 641 642 if (mBackgroundScanEventHandler != null) { 643 if ((mLastScanSettings.reportEvents 644 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 645 || (mLastScanSettings.reportEvents 646 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0 647 || (mLastScanSettings.reportEvents 648 == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 649 && (mBackgroundScanBuffer.size() 650 >= (mBackgroundScanBuffer.capacity() 651 * mLastScanSettings.reportPercentThreshold 652 / 100) 653 || mBackgroundScanBuffer.size() 654 >= mLastScanSettings.reportNumScansThreshold))) { 655 mBackgroundScanEventHandler 656 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 657 } 658 } 659 } 660 661 if (mLastScanSettings.singleScanActive 662 && mLastScanSettings.singleScanEventHandler != null) { 663 if (mLastScanSettings.reportSingleScanFullResults) { 664 for (ScanResult scanResult : singleScanResults) { 665 // ignore buckets scanned since there is only one bucket for a single scan 666 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 667 /* bucketsScanned */ 0); 668 } 669 } 670 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 671 mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0, 672 mLastScanSettings.singleScanFreqs.isAllChannels(), 673 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 674 mLastScanSettings.singleScanEventHandler 675 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 676 } 677 678 mLastScanSettings = null; 679 } 680 } 681 682 683 @Override 684 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 685 synchronized (mSettingsLock) { 686 WifiScanner.ScanData[] results = mBackgroundScanBuffer.get(); 687 if (flush) { 688 mBackgroundScanBuffer.clear(); 689 } 690 return results; 691 } 692 } 693 694 private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { 695 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 696 } 697 698 private void stopHwPnoScan() { 699 mHwPnoDebouncer.stopPnoScan(); 700 } 701 702 private void pauseHwPnoScan() { 703 mHwPnoDebouncer.forceStopPnoScan(); 704 } 705 706 private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) { 707 mHwPnoDebouncer.forceStopPnoScan(); 708 return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener); 709 } 710 711 /** 712 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 713 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 714 * @return true if HW PNO scan is required, false otherwise. 715 */ 716 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 717 return (!isConnectedPno & mHwPnoScanSupported); 718 } 719 720 private boolean isHwPnoScanRequired() { 721 if (mPnoSettings == null) return false; 722 return isHwPnoScanRequired(mPnoSettings.isConnected); 723 } 724 725 @Override 726 public boolean setHwPnoList(WifiNative.PnoSettings settings, 727 WifiNative.PnoEventHandler eventHandler) { 728 synchronized (mSettingsLock) { 729 if (mPnoSettings != null) { 730 Log.w(TAG, "Already running a PNO scan"); 731 return false; 732 } 733 mPnoEventHandler = eventHandler; 734 mPnoSettings = settings; 735 // TODO(b/36276738): Remove this after we fix b/36231150. 736 Log.d(TAG, "processPendingScans in request of setHwPnoList"); 737 738 // For wificond based PNO, we start the scan immediately when we set pno list. 739 processPendingScans(); 740 return true; 741 } 742 } 743 744 @Override 745 public boolean resetHwPnoList() { 746 synchronized (mSettingsLock) { 747 if (mPnoSettings == null) { 748 Log.w(TAG, "No PNO scan running"); 749 return false; 750 } 751 mPnoEventHandler = null; 752 mPnoSettings = null; 753 // For wificond based PNO, we stop the scan immediately when we reset pno list. 754 stopHwPnoScan(); 755 return true; 756 } 757 } 758 759 @Override 760 public boolean isHwPnoSupported(boolean isConnectedPno) { 761 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 762 return isHwPnoScanRequired(isConnectedPno); 763 } 764 765 @Override 766 public boolean shouldScheduleBackgroundScanForHwPno() { 767 return false; 768 } 769 770 private static class LastScanSettings { 771 public long startTime; 772 773 LastScanSettings(long startTime) { 774 this.startTime = startTime; 775 } 776 777 // Background settings 778 public boolean backgroundScanActive = false; 779 public int scanId; 780 public int maxAps; 781 public int reportEvents; 782 public int reportNumScansThreshold; 783 public int reportPercentThreshold; 784 785 public void setBackgroundScan(int scanId, int maxAps, int reportEvents, 786 int reportNumScansThreshold, int reportPercentThreshold) { 787 this.backgroundScanActive = true; 788 this.scanId = scanId; 789 this.maxAps = maxAps; 790 this.reportEvents = reportEvents; 791 this.reportNumScansThreshold = reportNumScansThreshold; 792 this.reportPercentThreshold = reportPercentThreshold; 793 } 794 795 // Single scan settings 796 public boolean singleScanActive = false; 797 public boolean reportSingleScanFullResults; 798 public ChannelCollection singleScanFreqs; 799 public WifiNative.ScanEventHandler singleScanEventHandler; 800 801 public void setSingleScan(boolean reportSingleScanFullResults, 802 ChannelCollection singleScanFreqs, 803 WifiNative.ScanEventHandler singleScanEventHandler) { 804 singleScanActive = true; 805 this.reportSingleScanFullResults = reportSingleScanFullResults; 806 this.singleScanFreqs = singleScanFreqs; 807 this.singleScanEventHandler = singleScanEventHandler; 808 } 809 810 public boolean hwPnoScanActive = false; 811 public WifiNative.PnoNetwork[] pnoNetworkList; 812 public WifiNative.PnoEventHandler pnoScanEventHandler; 813 814 public void setHwPnoScan( 815 WifiNative.PnoNetwork[] pnoNetworkList, 816 WifiNative.PnoEventHandler pnoScanEventHandler) { 817 hwPnoScanActive = true; 818 this.pnoNetworkList = pnoNetworkList; 819 this.pnoScanEventHandler = pnoScanEventHandler; 820 } 821 } 822 823 824 private static class ScanBuffer { 825 private final ArrayDeque<WifiScanner.ScanData> mBuffer; 826 private int mCapacity; 827 828 ScanBuffer(int capacity) { 829 mCapacity = capacity; 830 mBuffer = new ArrayDeque<>(mCapacity); 831 } 832 833 public int size() { 834 return mBuffer.size(); 835 } 836 837 public int capacity() { 838 return mCapacity; 839 } 840 841 public boolean isFull() { 842 return size() == mCapacity; 843 } 844 845 public void add(WifiScanner.ScanData scanData) { 846 if (isFull()) { 847 mBuffer.pollFirst(); 848 } 849 mBuffer.offerLast(scanData); 850 } 851 852 public void clear() { 853 mBuffer.clear(); 854 } 855 856 public WifiScanner.ScanData[] get() { 857 return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]); 858 } 859 } 860 861 /** 862 * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO 863 * state too often which is not handled very well by some drivers. 864 * Note: This is not thread safe! 865 */ 866 public static class HwPnoDebouncer { 867 public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor"; 868 private static final int MINIMUM_PNO_GAP_MS = 5 * 1000; 869 870 private final WifiNative mWifiNative; 871 private final AlarmManager mAlarmManager; 872 private final Handler mEventHandler; 873 private final Clock mClock; 874 private long mLastPnoChangeTimeStamp = -1L; 875 private boolean mExpectedPnoState = false; 876 private boolean mCurrentPnoState = false;; 877 private boolean mWaitForTimer = false; 878 private Listener mListener; 879 private WifiNative.PnoSettings mPnoSettings; 880 881 /** 882 * Interface used to indicate PNO scan notifications. 883 */ 884 public interface Listener { 885 /** 886 * Used to indicate a delayed PNO scan request failure. 887 */ 888 void onPnoScanFailed(); 889 } 890 891 public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager, 892 Handler eventHandler, Clock clock) { 893 mWifiNative = wifiNative; 894 mAlarmManager = alarmManager; 895 mEventHandler = eventHandler; 896 mClock = clock; 897 } 898 899 /** 900 * Enable PNO state in wificond 901 */ 902 private boolean startPnoScanInternal() { 903 if (mCurrentPnoState) { 904 if (DBG) Log.d(TAG, "PNO state is already enable"); 905 return true; 906 } 907 if (mPnoSettings == null) { 908 Log.e(TAG, "PNO state change to enable failed, no available Pno settings"); 909 return false; 910 } 911 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 912 if (mWifiNative.startPnoScan(mPnoSettings)) { 913 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable"); 914 mCurrentPnoState = true; 915 return true; 916 } else { 917 Log.e(TAG, "PNO state change to enable failed"); 918 mCurrentPnoState = false; 919 } 920 return false; 921 } 922 923 /** 924 * Disable PNO state in wificond 925 */ 926 private boolean stopPnoScanInternal() { 927 if (!mCurrentPnoState) { 928 if (DBG) Log.d(TAG, "PNO state is already disable"); 929 return true; 930 } 931 mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis(); 932 if (mWifiNative.stopPnoScan()) { 933 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable"); 934 mCurrentPnoState = false; 935 return true; 936 } else { 937 Log.e(TAG, "PNO state change to disable failed"); 938 mCurrentPnoState = false; 939 } 940 return false; 941 } 942 943 private final AlarmManager.OnAlarmListener mAlarmListener = 944 new AlarmManager.OnAlarmListener() { 945 public void onAlarm() { 946 if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState); 947 if (mExpectedPnoState) { 948 if (!startPnoScanInternal()) { 949 if (mListener != null) { 950 mListener.onPnoScanFailed(); 951 } 952 } 953 } else { 954 stopPnoScanInternal(); 955 } 956 mWaitForTimer = false; 957 } 958 }; 959 960 /** 961 * Enable/Disable PNO state. This method will debounce PNO scan requests. 962 * @param enable boolean indicating whether PNO is being enabled or disabled. 963 */ 964 private boolean setPnoState(boolean enable) { 965 boolean isSuccess = true; 966 mExpectedPnoState = enable; 967 if (!mWaitForTimer) { 968 long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp; 969 if (timeDifference >= MINIMUM_PNO_GAP_MS) { 970 if (enable) { 971 isSuccess = startPnoScanInternal(); 972 } else { 973 isSuccess = stopPnoScanInternal(); 974 } 975 } else { 976 long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference; 977 Log.d(TAG, "Start PNO timer with delay " + alarmTimeout); 978 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 979 mClock.getElapsedSinceBootMillis() + alarmTimeout, 980 PNO_DEBOUNCER_ALARM_TAG, 981 mAlarmListener, mEventHandler); 982 mWaitForTimer = true; 983 } 984 } 985 return isSuccess; 986 } 987 988 /** 989 * Start PNO scan 990 */ 991 public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) { 992 if (DBG) Log.d(TAG, "Starting PNO scan"); 993 mListener = listener; 994 mPnoSettings = pnoSettings; 995 if (!setPnoState(true)) { 996 mListener = null; 997 return false; 998 } 999 return true; 1000 } 1001 1002 /** 1003 * Stop PNO scan 1004 */ 1005 public void stopPnoScan() { 1006 if (DBG) Log.d(TAG, "Stopping PNO scan"); 1007 setPnoState(false); 1008 mListener = null; 1009 } 1010 1011 /** 1012 * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO 1013 * scan immediately. 1014 */ 1015 public void forceStopPnoScan() { 1016 if (DBG) Log.d(TAG, "Force stopping Pno scan"); 1017 // Cancel the debounce timer and stop PNO scan. 1018 if (mWaitForTimer) { 1019 mAlarmManager.cancel(mAlarmListener); 1020 mWaitForTimer = false; 1021 } 1022 stopPnoScanInternal(); 1023 } 1024 } 1025} 1026