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 void processPendingScans() { 333 synchronized (mSettingsLock) { 334 // Wait for the active scan result to come back to reschedule other scans, 335 // unless if HW pno scan is running. Hw PNO scans are paused it if there 336 // are other pending scans, 337 if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) { 338 return; 339 } 340 341 ChannelCollection allFreqs = mChannelHelper.createChannelCollection(); 342 Set<Integer> hiddenNetworkIdSet = new HashSet<Integer>(); 343 final LastScanSettings newScanSettings = 344 new LastScanSettings(mClock.elapsedRealtime()); 345 346 // Update scan settings if there is a pending scan 347 if (!mBackgroundScanPaused) { 348 if (mPendingBackgroundScanSettings != null) { 349 mBackgroundScanSettings = mPendingBackgroundScanSettings; 350 mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler; 351 mNextBackgroundScanPeriod = 0; 352 mPendingBackgroundScanSettings = null; 353 mPendingBackgroundScanEventHandler = null; 354 mBackgroundScanPeriodPending = true; 355 } 356 if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) { 357 int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch 358 for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets; 359 ++bucket_id) { 360 WifiNative.BucketSettings bucket = 361 mBackgroundScanSettings.buckets[bucket_id]; 362 if (mNextBackgroundScanPeriod % (bucket.period_ms 363 / mBackgroundScanSettings.base_period_ms) == 0) { 364 if ((bucket.report_events 365 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { 366 reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 367 } 368 if ((bucket.report_events 369 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 370 reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; 371 } 372 // only no batch if all buckets specify it 373 if ((bucket.report_events 374 & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 375 reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; 376 } 377 378 allFreqs.addChannels(bucket); 379 } 380 } 381 if (!allFreqs.isEmpty()) { 382 newScanSettings.setBackgroundScan(mNextBackgroundScanId++, 383 mBackgroundScanSettings.max_ap_per_scan, reportEvents, 384 mBackgroundScanSettings.report_threshold_num_scans, 385 mBackgroundScanSettings.report_threshold_percent); 386 } 387 388 int[] hiddenNetworkIds = mBackgroundScanSettings.hiddenNetworkIds; 389 if (hiddenNetworkIds != null) { 390 int numHiddenNetworkIds = Math.min(hiddenNetworkIds.length, 391 MAX_HIDDEN_NETWORK_IDS_PER_SCAN); 392 for (int i = 0; i < numHiddenNetworkIds; i++) { 393 hiddenNetworkIdSet.add(hiddenNetworkIds[i]); 394 } 395 } 396 397 mNextBackgroundScanPeriod++; 398 mBackgroundScanPeriodPending = false; 399 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 400 mClock.elapsedRealtime() + mBackgroundScanSettings.base_period_ms, 401 BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler); 402 } 403 } 404 405 if (mPendingSingleScanSettings != null) { 406 boolean reportFullResults = false; 407 ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection(); 408 for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) { 409 WifiNative.BucketSettings bucketSettings = 410 mPendingSingleScanSettings.buckets[i]; 411 if ((bucketSettings.report_events 412 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 413 reportFullResults = true; 414 } 415 singleScanFreqs.addChannels(bucketSettings); 416 allFreqs.addChannels(bucketSettings); 417 } 418 newScanSettings.setSingleScan(reportFullResults, singleScanFreqs, 419 mPendingSingleScanEventHandler); 420 int[] hiddenNetworkIds = mPendingSingleScanSettings.hiddenNetworkIds; 421 if (hiddenNetworkIds != null) { 422 int numHiddenNetworkIds = Math.min(hiddenNetworkIds.length, 423 MAX_HIDDEN_NETWORK_IDS_PER_SCAN); 424 for (int i = 0; i < numHiddenNetworkIds; i++) { 425 hiddenNetworkIdSet.add(hiddenNetworkIds[i]); 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, hiddenNetworkIdSet); 437 if (success) { 438 // TODO handle scan timeout 439 if (DBG) { 440 Log.d(TAG, "Starting wifi scan for freqs=" + freqs 441 + ", background=" + newScanSettings.backgroundScanActive 442 + ", single=" + newScanSettings.singleScanActive); 443 } 444 mLastScanSettings = newScanSettings; 445 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 446 mClock.elapsedRealtime() + SCAN_TIMEOUT_MS, 447 TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); 448 } else { 449 Log.e(TAG, "Failed to start scan, freqs=" + freqs); 450 // indicate scan failure async 451 mEventHandler.post(new Runnable() { 452 public void run() { 453 if (newScanSettings.singleScanEventHandler != null) { 454 newScanSettings.singleScanEventHandler 455 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 456 } 457 } 458 }); 459 // TODO(b/27769665) background scans should be failed too if scans fail enough 460 } 461 } else if (isHwPnoScanRequired()) { 462 newScanSettings.setHwPnoScan(mPnoEventHandler); 463 if (startHwPnoScan()) { 464 mLastScanSettings = newScanSettings; 465 } else { 466 Log.e(TAG, "Failed to start PNO scan"); 467 // indicate scan failure async 468 mEventHandler.post(new Runnable() { 469 public void run() { 470 if (mPnoEventHandler != null) { 471 mPnoEventHandler.onPnoScanFailed(); 472 } 473 // Clean up PNO state, we don't want to continue PNO scanning. 474 mPnoSettings = null; 475 mPnoEventHandler = null; 476 } 477 }); 478 } 479 } 480 } 481 } 482 483 @Override 484 public boolean handleMessage(Message msg) { 485 switch(msg.what) { 486 case WifiMonitor.SCAN_FAILED_EVENT: 487 Log.w(TAG, "Scan failed"); 488 mAlarmManager.cancel(mScanTimeoutListener); 489 reportScanFailure(); 490 processPendingScans(); 491 break; 492 case WifiMonitor.SCAN_RESULTS_EVENT: 493 mAlarmManager.cancel(mScanTimeoutListener); 494 pollLatestScanData(); 495 processPendingScans(); 496 break; 497 default: 498 // ignore unknown event 499 } 500 return true; 501 } 502 503 private void reportScanFailure() { 504 synchronized (mSettingsLock) { 505 if (mLastScanSettings != null) { 506 if (mLastScanSettings.singleScanEventHandler != null) { 507 mLastScanSettings.singleScanEventHandler 508 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 509 } 510 // TODO(b/27769665) background scans should be failed too if scans fail enough 511 mLastScanSettings = null; 512 } 513 } 514 } 515 516 private void reportPnoScanFailure() { 517 synchronized (mSettingsLock) { 518 if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) { 519 if (mLastScanSettings.pnoScanEventHandler != null) { 520 mLastScanSettings.pnoScanEventHandler.onPnoScanFailed(); 521 } 522 // Clean up PNO state, we don't want to continue PNO scanning. 523 mPnoSettings = null; 524 mPnoEventHandler = null; 525 mLastScanSettings = null; 526 } 527 } 528 } 529 530 private void pollLatestScanData() { 531 synchronized (mSettingsLock) { 532 if (mLastScanSettings == null) { 533 // got a scan before we started scanning or after scan was canceled 534 return; 535 } 536 537 if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId); 538 ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults(); 539 List<ScanResult> singleScanResults = new ArrayList<>(); 540 List<ScanResult> backgroundScanResults = new ArrayList<>(); 541 List<ScanResult> hwPnoScanResults = new ArrayList<>(); 542 for (int i = 0; i < nativeResults.size(); ++i) { 543 ScanResult result = nativeResults.get(i).getScanResult(); 544 long timestamp_ms = result.timestamp / 1000; // convert us -> ms 545 if (timestamp_ms > mLastScanSettings.startTime) { 546 if (mLastScanSettings.backgroundScanActive) { 547 backgroundScanResults.add(result); 548 } 549 if (mLastScanSettings.singleScanActive 550 && mLastScanSettings.singleScanFreqs.containsChannel( 551 result.frequency)) { 552 singleScanResults.add(result); 553 } 554 if (mLastScanSettings.hwPnoScanActive) { 555 hwPnoScanResults.add(result); 556 } 557 } else { 558 // was a cached result in wpa_supplicant 559 } 560 } 561 562 if (mLastScanSettings.backgroundScanActive) { 563 if (mBackgroundScanEventHandler != null) { 564 if ((mLastScanSettings.reportEvents 565 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 566 for (ScanResult scanResult : backgroundScanResults) { 567 // TODO(b/27506257): Fill in correct bucketsScanned value 568 mBackgroundScanEventHandler.onFullScanResult(scanResult, 0); 569 } 570 } 571 } 572 573 Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR); 574 ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps, 575 backgroundScanResults.size())]; 576 for (int i = 0; i < scanResultsArray.length; ++i) { 577 scanResultsArray[i] = backgroundScanResults.get(i); 578 } 579 580 if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { 581 // TODO(b/27506257): Fill in correct bucketsScanned value 582 mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 583 scanResultsArray)); 584 } 585 586 if (mBackgroundScanEventHandler != null) { 587 if ((mLastScanSettings.reportEvents 588 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 589 || (mLastScanSettings.reportEvents 590 & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0 591 || (mLastScanSettings.reportEvents 592 == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL 593 && (mBackgroundScanBuffer.size() 594 >= (mBackgroundScanBuffer.capacity() 595 * mLastScanSettings.reportPercentThreshold 596 / 100) 597 || mBackgroundScanBuffer.size() 598 >= mLastScanSettings.reportNumScansThreshold))) { 599 mBackgroundScanEventHandler 600 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 601 } 602 } 603 604 if (mHotlistHandler != null) { 605 int event = mHotlistChangeBuffer.processScan(backgroundScanResults); 606 if ((event & ChangeBuffer.EVENT_FOUND) != 0) { 607 mHotlistHandler.onHotlistApFound( 608 mHotlistChangeBuffer.getLastResults(ChangeBuffer.EVENT_FOUND)); 609 } 610 if ((event & ChangeBuffer.EVENT_LOST) != 0) { 611 mHotlistHandler.onHotlistApLost( 612 mHotlistChangeBuffer.getLastResults(ChangeBuffer.EVENT_LOST)); 613 } 614 } 615 } 616 617 if (mLastScanSettings.singleScanActive 618 && mLastScanSettings.singleScanEventHandler != null) { 619 if (mLastScanSettings.reportSingleScanFullResults) { 620 for (ScanResult scanResult : singleScanResults) { 621 // ignore buckets scanned since there is only one bucket for a single scan 622 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 623 /* bucketsScanned */ 0); 624 } 625 } 626 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 627 mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 628 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 629 mLastScanSettings.singleScanEventHandler 630 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 631 } 632 633 if (mLastScanSettings.hwPnoScanActive 634 && mLastScanSettings.pnoScanEventHandler != null) { 635 ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()]; 636 for (int i = 0; i < pnoScanResultsArray.length; ++i) { 637 pnoScanResultsArray[i] = hwPnoScanResults.get(i); 638 } 639 mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); 640 } 641 642 mLastScanSettings = null; 643 } 644 } 645 646 647 @Override 648 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 649 synchronized (mSettingsLock) { 650 WifiScanner.ScanData[] results = mBackgroundScanBuffer.get(); 651 if (flush) { 652 mBackgroundScanBuffer.clear(); 653 } 654 return results; 655 } 656 } 657 658 private boolean setNetworkPriorities(WifiNative.PnoNetwork[] networkList) { 659 if (networkList != null) { 660 if (DBG) Log.i(TAG, "Enable network and Set priorities for PNO."); 661 for (WifiNative.PnoNetwork network : networkList) { 662 if (!mWifiNative.setNetworkVariable(network.networkId, 663 WifiConfiguration.priorityVarName, 664 Integer.toString(network.priority))) { 665 Log.e(TAG, "Set priority failed for: " + network.networkId); 666 return false; 667 } 668 if (!mWifiNative.enableNetworkWithoutConnect(network.networkId)) { 669 Log.e(TAG, "Enable network failed for: " + network.networkId); 670 return false; 671 } 672 } 673 } 674 return true; 675 } 676 677 private boolean startHwPnoScan() { 678 return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener); 679 } 680 681 private void stopHwPnoScan() { 682 mHwPnoDebouncer.stopPnoScan(); 683 } 684 685 private void pauseHwPnoScan() { 686 mHwPnoDebouncer.forceStopPnoScan(); 687 } 688 689 /** 690 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 691 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 692 * @return true if HW PNO scan is required, false otherwise. 693 */ 694 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 695 return (!isConnectedPno & mHwPnoScanSupported); 696 } 697 698 private boolean isHwPnoScanRequired() { 699 if (mPnoSettings == null) return false; 700 return isHwPnoScanRequired(mPnoSettings.isConnected); 701 } 702 703 @Override 704 public boolean setHwPnoList(WifiNative.PnoSettings settings, 705 WifiNative.PnoEventHandler eventHandler) { 706 synchronized (mSettingsLock) { 707 if (mPnoSettings != null) { 708 Log.w(TAG, "Already running a PNO scan"); 709 return false; 710 } 711 mPnoEventHandler = eventHandler; 712 mPnoSettings = settings; 713 if (!setNetworkPriorities(settings.networkList)) return false; 714 // For supplicant based PNO, we start the scan immediately when we set pno list. 715 processPendingScans(); 716 return true; 717 } 718 } 719 720 @Override 721 public boolean resetHwPnoList() { 722 synchronized (mSettingsLock) { 723 if (mPnoSettings == null) { 724 Log.w(TAG, "No PNO scan running"); 725 return false; 726 } 727 mPnoEventHandler = null; 728 mPnoSettings = null; 729 // For supplicant based PNO, we stop the scan immediately when we reset pno list. 730 stopHwPnoScan(); 731 return true; 732 } 733 } 734 735 @Override 736 public boolean isHwPnoSupported(boolean isConnectedPno) { 737 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 738 return isHwPnoScanRequired(isConnectedPno); 739 } 740 741 @Override 742 public boolean shouldScheduleBackgroundScanForHwPno() { 743 return false; 744 } 745 746 @Override 747 public boolean setHotlist(WifiScanner.HotlistSettings settings, 748 WifiNative.HotlistEventHandler eventHandler) { 749 if (settings == null || eventHandler == null) { 750 return false; 751 } 752 synchronized (mSettingsLock) { 753 mHotlistHandler = eventHandler; 754 mHotlistChangeBuffer.setSettings(settings.bssidInfos, settings.apLostThreshold, 1); 755 return true; 756 } 757 } 758 759 @Override 760 public void resetHotlist() { 761 synchronized (mSettingsLock) { 762 mHotlistChangeBuffer.clearSettings(); 763 mHotlistHandler = null; 764 } 765 } 766 767 /* 768 * Significant Wifi Change API is not implemented 769 */ 770 @Override 771 public boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings, 772 WifiNative.SignificantWifiChangeEventHandler handler) { 773 return false; 774 } 775 @Override 776 public void untrackSignificantWifiChange() {} 777 778 779 private static class LastScanSettings { 780 public long startTime; 781 782 public LastScanSettings(long startTime) { 783 this.startTime = startTime; 784 } 785 786 // Background settings 787 public boolean backgroundScanActive = false; 788 public int scanId; 789 public int maxAps; 790 public int reportEvents; 791 public int reportNumScansThreshold; 792 public int reportPercentThreshold; 793 794 public void setBackgroundScan(int scanId, int maxAps, int reportEvents, 795 int reportNumScansThreshold, int reportPercentThreshold) { 796 this.backgroundScanActive = true; 797 this.scanId = scanId; 798 this.maxAps = maxAps; 799 this.reportEvents = reportEvents; 800 this.reportNumScansThreshold = reportNumScansThreshold; 801 this.reportPercentThreshold = reportPercentThreshold; 802 } 803 804 // Single scan settings 805 public boolean singleScanActive = false; 806 public boolean reportSingleScanFullResults; 807 public ChannelCollection singleScanFreqs; 808 public WifiNative.ScanEventHandler singleScanEventHandler; 809 810 public void setSingleScan(boolean reportSingleScanFullResults, 811 ChannelCollection singleScanFreqs, 812 WifiNative.ScanEventHandler singleScanEventHandler) { 813 singleScanActive = true; 814 this.reportSingleScanFullResults = reportSingleScanFullResults; 815 this.singleScanFreqs = singleScanFreqs; 816 this.singleScanEventHandler = singleScanEventHandler; 817 } 818 819 public boolean hwPnoScanActive = false; 820 public WifiNative.PnoEventHandler pnoScanEventHandler; 821 822 public void setHwPnoScan(WifiNative.PnoEventHandler pnoScanEventHandler) { 823 hwPnoScanActive = true; 824 this.pnoScanEventHandler = pnoScanEventHandler; 825 } 826 } 827 828 829 private static class ScanBuffer { 830 private final ArrayDeque<WifiScanner.ScanData> mBuffer; 831 private int mCapacity; 832 833 public ScanBuffer(int capacity) { 834 mCapacity = capacity; 835 mBuffer = new ArrayDeque<>(mCapacity); 836 } 837 838 public int size() { 839 return mBuffer.size(); 840 } 841 842 public int capacity() { 843 return mCapacity; 844 } 845 846 public boolean isFull() { 847 return size() == mCapacity; 848 } 849 850 public void add(WifiScanner.ScanData scanData) { 851 if (isFull()) { 852 mBuffer.pollFirst(); 853 } 854 mBuffer.offerLast(scanData); 855 } 856 857 public void clear() { 858 mBuffer.clear(); 859 } 860 861 public WifiScanner.ScanData[] get() { 862 return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]); 863 } 864 } 865 866 private static class ChangeBuffer { 867 public static int EVENT_NONE = 0; 868 public static int EVENT_LOST = 1; 869 public static int EVENT_FOUND = 2; 870 871 public static int STATE_FOUND = 0; 872 873 private WifiScanner.BssidInfo[] mBssidInfos = null; 874 private int mApLostThreshold; 875 private int mMinEvents; 876 private int[] mLostCount = null; 877 private ScanResult[] mMostRecentResult = null; 878 private int[] mPendingEvent = null; 879 private boolean mFiredEvents = false; 880 881 private static ScanResult findResult(List<ScanResult> results, String bssid) { 882 for (int i = 0; i < results.size(); ++i) { 883 if (bssid.equalsIgnoreCase(results.get(i).BSSID)) { 884 return results.get(i); 885 } 886 } 887 return null; 888 } 889 890 public void setSettings(WifiScanner.BssidInfo[] bssidInfos, int apLostThreshold, 891 int minEvents) { 892 mBssidInfos = bssidInfos; 893 if (apLostThreshold <= 0) { 894 mApLostThreshold = 1; 895 } else { 896 mApLostThreshold = apLostThreshold; 897 } 898 mMinEvents = minEvents; 899 if (bssidInfos != null) { 900 mLostCount = new int[bssidInfos.length]; 901 Arrays.fill(mLostCount, mApLostThreshold); // default to lost 902 mMostRecentResult = new ScanResult[bssidInfos.length]; 903 mPendingEvent = new int[bssidInfos.length]; 904 mFiredEvents = false; 905 } else { 906 mLostCount = null; 907 mMostRecentResult = null; 908 mPendingEvent = null; 909 } 910 } 911 912 public void clearSettings() { 913 setSettings(null, 0, 0); 914 } 915 916 /** 917 * Get the most recent scan results for APs that triggered the given event on the last call 918 * to {@link #processScan}. 919 */ 920 public ScanResult[] getLastResults(int event) { 921 ArrayList<ScanResult> results = new ArrayList<>(); 922 for (int i = 0; i < mLostCount.length; ++i) { 923 if (mPendingEvent[i] == event) { 924 results.add(mMostRecentResult[i]); 925 } 926 } 927 return results.toArray(new ScanResult[results.size()]); 928 } 929 930 /** 931 * Process the supplied scan results and determine if any events should be generated based 932 * on the configured settings 933 * @return The events that occurred 934 */ 935 public int processScan(List<ScanResult> scanResults) { 936 if (mBssidInfos == null) { 937 return EVENT_NONE; 938 } 939 940 // clear events from last time 941 if (mFiredEvents) { 942 mFiredEvents = false; 943 for (int i = 0; i < mLostCount.length; ++i) { 944 mPendingEvent[i] = EVENT_NONE; 945 } 946 } 947 948 int eventCount = 0; 949 int eventType = EVENT_NONE; 950 for (int i = 0; i < mLostCount.length; ++i) { 951 ScanResult result = findResult(scanResults, mBssidInfos[i].bssid); 952 int rssi = Integer.MIN_VALUE; 953 if (result != null) { 954 mMostRecentResult[i] = result; 955 rssi = result.level; 956 } 957 958 if (rssi < mBssidInfos[i].low) { 959 if (mLostCount[i] < mApLostThreshold) { 960 mLostCount[i]++; 961 962 if (mLostCount[i] >= mApLostThreshold) { 963 if (mPendingEvent[i] == EVENT_FOUND) { 964 mPendingEvent[i] = EVENT_NONE; 965 } else { 966 mPendingEvent[i] = EVENT_LOST; 967 } 968 } 969 } 970 } else { 971 if (mLostCount[i] >= mApLostThreshold) { 972 if (mPendingEvent[i] == EVENT_LOST) { 973 mPendingEvent[i] = EVENT_NONE; 974 } else { 975 mPendingEvent[i] = EVENT_FOUND; 976 } 977 } 978 mLostCount[i] = STATE_FOUND; 979 } 980 if (DBG) { 981 Log.d(TAG, "ChangeBuffer BSSID: " + mBssidInfos[i].bssid + "=" + mLostCount[i] 982 + ", " + mPendingEvent[i] + ", rssi=" + rssi); 983 } 984 if (mPendingEvent[i] != EVENT_NONE) { 985 ++eventCount; 986 eventType |= mPendingEvent[i]; 987 } 988 } 989 if (DBG) Log.d(TAG, "ChangeBuffer events count=" + eventCount + ": " + eventType); 990 if (eventCount >= mMinEvents) { 991 mFiredEvents = true; 992 return eventType; 993 } 994 return EVENT_NONE; 995 } 996 } 997 998 /** 999 * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO 1000 * state too often which is not handled very well by some drivers. 1001 * Note: This is not thread safe! 1002 */ 1003 public static class HwPnoDebouncer { 1004 public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor"; 1005 private static final int MINIMUM_PNO_GAP_MS = 5 * 1000; 1006 1007 private final WifiNative mWifiNative; 1008 private final AlarmManager mAlarmManager; 1009 private final Handler mEventHandler; 1010 private final Clock mClock; 1011 private long mLastPnoChangeTimeStamp = -1L; 1012 private boolean mExpectedPnoState = false; 1013 private boolean mCurrentPnoState = false;; 1014 private boolean mWaitForTimer = false; 1015 private Listener mListener; 1016 1017 /** 1018 * Interface used to indicate PNO scan notifications. 1019 */ 1020 public interface Listener { 1021 /** 1022 * Used to indicate a delayed PNO scan request failure. 1023 */ 1024 void onPnoScanFailed(); 1025 } 1026 1027 public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager, 1028 Handler eventHandler, Clock clock) { 1029 mWifiNative = wifiNative; 1030 mAlarmManager = alarmManager; 1031 mEventHandler = eventHandler; 1032 mClock = clock; 1033 } 1034 1035 /** 1036 * Enable/Disable PNO state in wpa_supplicant 1037 * @param enable boolean indicating whether PNO is being enabled or disabled. 1038 */ 1039 private boolean updatePnoState(boolean enable) { 1040 if (mCurrentPnoState == enable) { 1041 if (DBG) Log.d(TAG, "PNO state is already " + enable); 1042 return true; 1043 } 1044 1045 mLastPnoChangeTimeStamp = mClock.elapsedRealtime(); 1046 if (mWifiNative.setPnoScan(enable)) { 1047 Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable); 1048 mCurrentPnoState = enable; 1049 return true; 1050 } else { 1051 Log.e(TAG, "PNO state change to " + enable + " failed"); 1052 return false; 1053 } 1054 } 1055 1056 private final AlarmManager.OnAlarmListener mAlarmListener = 1057 new AlarmManager.OnAlarmListener() { 1058 public void onAlarm() { 1059 if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState); 1060 if (!updatePnoState(mExpectedPnoState)) { 1061 if (mListener != null) { 1062 mListener.onPnoScanFailed(); 1063 } 1064 } 1065 mWaitForTimer = false; 1066 } 1067 }; 1068 1069 /** 1070 * Enable/Disable PNO state. This method will debounce PNO scan requests. 1071 * @param enable boolean indicating whether PNO is being enabled or disabled. 1072 */ 1073 private boolean setPnoState(boolean enable) { 1074 boolean isSuccess = true; 1075 mExpectedPnoState = enable; 1076 if (!mWaitForTimer) { 1077 long timeDifference = mClock.elapsedRealtime() - mLastPnoChangeTimeStamp; 1078 if (timeDifference >= MINIMUM_PNO_GAP_MS) { 1079 isSuccess = updatePnoState(enable); 1080 } else { 1081 long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference; 1082 Log.d(TAG, "Start PNO timer with delay " + alarmTimeout); 1083 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1084 mClock.elapsedRealtime() + alarmTimeout, PNO_DEBOUNCER_ALARM_TAG, 1085 mAlarmListener, mEventHandler); 1086 mWaitForTimer = true; 1087 } 1088 } 1089 return isSuccess; 1090 } 1091 1092 /** 1093 * Start PNO scan 1094 */ 1095 public boolean startPnoScan(Listener listener) { 1096 if (DBG) Log.d(TAG, "Starting PNO scan"); 1097 mListener = listener; 1098 if (!setPnoState(true)) { 1099 mListener = null; 1100 return false; 1101 } 1102 return true; 1103 } 1104 1105 /** 1106 * Stop PNO scan 1107 */ 1108 public void stopPnoScan() { 1109 if (DBG) Log.d(TAG, "Stopping PNO scan"); 1110 setPnoState(false); 1111 mListener = null; 1112 } 1113 1114 /** 1115 * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO 1116 * scan immediately. 1117 */ 1118 public void forceStopPnoScan() { 1119 if (mCurrentPnoState) { 1120 if (DBG) Log.d(TAG, "Force stopping Pno scan"); 1121 // Cancel the debounce timer and stop PNO scan. 1122 if (mWaitForTimer) { 1123 mAlarmManager.cancel(mAlarmListener); 1124 mWaitForTimer = false; 1125 } 1126 updatePnoState(false); 1127 } 1128 } 1129 } 1130} 1131