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