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 17 package com.android.server.wifi.scanner; 18 19 import android.app.AlarmManager; 20 import android.content.Context; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.WifiScanner; 23 import android.net.wifi.WifiScanner.WifiBandIndex; 24 import android.net.wifi.util.ScanResultUtil; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.util.Log; 29 30 import com.android.server.wifi.Clock; 31 import com.android.server.wifi.ScanDetail; 32 import com.android.server.wifi.WifiMonitor; 33 import com.android.server.wifi.WifiNative; 34 import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; 35 import com.android.server.wifi.util.NativeUtil; 36 import com.android.wifi.resources.R; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 46 import javax.annotation.concurrent.GuardedBy; 47 48 /** 49 * Implementation of the WifiScanner HAL API that uses wificond to perform all scans 50 * @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method. 51 */ 52 public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback { 53 private static final String TAG = "WificondScannerImpl"; 54 private static final boolean DBG = false; 55 56 public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout"; 57 // Default number of networks that can be specified to wificond per scan request 58 public static final int DEFAULT_NUM_HIDDEN_NETWORK_IDS_PER_SCAN = 16; 59 60 private static final int SCAN_BUFFER_CAPACITY = 10; 61 private static final int MAX_APS_PER_SCAN = 32; 62 private static final int MAX_SCAN_BUCKETS = 16; 63 64 private final Context mContext; 65 private final WifiNative mWifiNative; 66 private final WifiMonitor mWifiMonitor; 67 private final AlarmManager mAlarmManager; 68 private final Handler mEventHandler; 69 private final ChannelHelper mChannelHelper; 70 private final Clock mClock; 71 72 private final Object mSettingsLock = new Object(); 73 74 private ArrayList<ScanDetail> mNativeScanResults; 75 private ArrayList<ScanDetail> mNativePnoScanResults; 76 private WifiScanner.ScanData mLatestSingleScanResult = 77 new WifiScanner.ScanData(0, 0, new ScanResult[0]); 78 private int mMaxNumScanSsids = -1; 79 private int mNextHiddenNetworkScanId = 0; 80 81 // Settings for the currently running single scan, null if no scan active 82 private LastScanSettings mLastScanSettings = null; 83 // Settings for the currently running pno scan, null if no scan active 84 private LastPnoScanSettings mLastPnoScanSettings = null; 85 86 /** 87 * Duration to wait before timing out a scan. 88 * 89 * The expected behavior is that the hardware will return a failed scan if it does not 90 * complete, but timeout just in case it does not. 91 */ 92 private static final long SCAN_TIMEOUT_MS = 15000; 93 94 @GuardedBy("mSettingsLock") 95 private AlarmManager.OnAlarmListener mScanTimeoutListener; 96 WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative, WifiMonitor wifiMonitor, ChannelHelper channelHelper, Looper looper, Clock clock)97 public WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative, 98 WifiMonitor wifiMonitor, ChannelHelper channelHelper, 99 Looper looper, Clock clock) { 100 super(ifaceName); 101 mContext = context; 102 mWifiNative = wifiNative; 103 mWifiMonitor = wifiMonitor; 104 mChannelHelper = channelHelper; 105 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 106 mEventHandler = new Handler(looper, this); 107 mClock = clock; 108 109 wifiMonitor.registerHandler(getIfaceName(), 110 WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); 111 wifiMonitor.registerHandler(getIfaceName(), 112 WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler); 113 wifiMonitor.registerHandler(getIfaceName(), 114 WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); 115 } 116 117 @Override cleanup()118 public void cleanup() { 119 synchronized (mSettingsLock) { 120 cancelScanTimeout(); 121 reportScanFailure(); 122 stopHwPnoScan(); 123 mMaxNumScanSsids = -1; 124 mNextHiddenNetworkScanId = 0; 125 mLastScanSettings = null; // finally clear any active scan 126 mLastPnoScanSettings = null; // finally clear any active scan 127 mWifiMonitor.deregisterHandler(getIfaceName(), 128 WifiMonitor.SCAN_FAILED_EVENT, mEventHandler); 129 mWifiMonitor.deregisterHandler(getIfaceName(), 130 WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler); 131 mWifiMonitor.deregisterHandler(getIfaceName(), 132 WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler); 133 } 134 } 135 136 @Override getScanCapabilities(WifiNative.ScanCapabilities capabilities)137 public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) { 138 capabilities.max_scan_cache_size = Integer.MAX_VALUE; 139 capabilities.max_scan_buckets = MAX_SCAN_BUCKETS; 140 capabilities.max_ap_cache_per_scan = MAX_APS_PER_SCAN; 141 capabilities.max_rssi_sample_size = 8; 142 capabilities.max_scan_reporting_threshold = SCAN_BUFFER_CAPACITY; 143 return true; 144 } 145 146 @Override getChannelHelper()147 public ChannelHelper getChannelHelper() { 148 return mChannelHelper; 149 } 150 151 @Override startSingleScan(WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler)152 public boolean startSingleScan(WifiNative.ScanSettings settings, 153 WifiNative.ScanEventHandler eventHandler) { 154 if (eventHandler == null || settings == null) { 155 Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings 156 + ",eventHandler=" + eventHandler); 157 return false; 158 } 159 synchronized (mSettingsLock) { 160 if (mLastScanSettings != null) { 161 Log.w(TAG, "A single scan is already running"); 162 return false; 163 } 164 165 ChannelCollection allFreqs = mChannelHelper.createChannelCollection(); 166 boolean reportFullResults = false; 167 168 for (int i = 0; i < settings.num_buckets; ++i) { 169 WifiNative.BucketSettings bucketSettings = settings.buckets[i]; 170 if ((bucketSettings.report_events 171 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { 172 reportFullResults = true; 173 } 174 allFreqs.addChannels(bucketSettings); 175 } 176 177 List<String> hiddenNetworkSSIDSet = new ArrayList<>(); 178 if (settings.hiddenNetworks != null) { 179 boolean executeRoundRobin = true; 180 int maxNumScanSsids = mMaxNumScanSsids; 181 if (maxNumScanSsids <= 0) { 182 // Subtract 1 to account for the wildcard/broadcast probe request that 183 // wificond adds to the scan set. 184 mMaxNumScanSsids = mWifiNative.getMaxSsidsPerScan(getIfaceName()) - 1; 185 if (mMaxNumScanSsids > 0) { 186 maxNumScanSsids = mMaxNumScanSsids; 187 } else { 188 maxNumScanSsids = DEFAULT_NUM_HIDDEN_NETWORK_IDS_PER_SCAN; 189 executeRoundRobin = false; 190 } 191 } 192 int numHiddenNetworksPerScan = 193 Math.min(settings.hiddenNetworks.length, maxNumScanSsids); 194 if (numHiddenNetworksPerScan == settings.hiddenNetworks.length 195 || mNextHiddenNetworkScanId >= settings.hiddenNetworks.length 196 || !executeRoundRobin) { 197 mNextHiddenNetworkScanId = 0; 198 } 199 if (DBG) { 200 Log.d(TAG, "Scanning for " + numHiddenNetworksPerScan + " out of " 201 + settings.hiddenNetworks.length + " total hidden networks"); 202 Log.d(TAG, "Scan hidden networks starting at id=" + mNextHiddenNetworkScanId); 203 } 204 205 int id = mNextHiddenNetworkScanId; 206 for (int i = 0; i < numHiddenNetworksPerScan; i++, id++) { 207 hiddenNetworkSSIDSet.add( 208 settings.hiddenNetworks[id % settings.hiddenNetworks.length].ssid); 209 } 210 mNextHiddenNetworkScanId = id % settings.hiddenNetworks.length; 211 } 212 mLastScanSettings = new LastScanSettings( 213 mClock.getElapsedSinceBootNanos(), 214 reportFullResults, allFreqs, eventHandler); 215 216 boolean success = false; 217 Set<Integer> freqs = Collections.emptySet(); 218 if (!allFreqs.isEmpty()) { 219 freqs = allFreqs.getScanFreqs(); 220 success = mWifiNative.scan( 221 getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet, 222 settings.enable6GhzRnr); 223 if (!success) { 224 Log.e(TAG, "Failed to start scan, freqs=" + freqs); 225 } 226 } else { 227 // There is a scan request but no available channels could be scanned for. 228 // We regard it as a scan failure in this case. 229 Log.e(TAG, "Failed to start scan because there is no available channel to scan"); 230 } 231 if (success) { 232 if (DBG) { 233 Log.d(TAG, "Starting wifi scan for freqs=" + freqs 234 + " on iface " + getIfaceName()); 235 } 236 237 mScanTimeoutListener = new AlarmManager.OnAlarmListener() { 238 @Override public void onAlarm() { 239 handleScanTimeout(); 240 } 241 }; 242 243 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 244 mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, 245 TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); 246 } else { 247 // indicate scan failure async 248 mEventHandler.post(() -> reportScanFailure()); 249 } 250 251 return true; 252 } 253 } 254 255 @Override getLatestSingleScanResults()256 public WifiScanner.ScanData getLatestSingleScanResults() { 257 return mLatestSingleScanResult; 258 } 259 260 @Override startBatchedScan(WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler)261 public boolean startBatchedScan(WifiNative.ScanSettings settings, 262 WifiNative.ScanEventHandler eventHandler) { 263 Log.w(TAG, "startBatchedScan() is not supported"); 264 return false; 265 } 266 267 @Override stopBatchedScan()268 public void stopBatchedScan() { 269 Log.w(TAG, "stopBatchedScan() is not supported"); 270 } 271 272 @Override pauseBatchedScan()273 public void pauseBatchedScan() { 274 Log.w(TAG, "pauseBatchedScan() is not supported"); 275 } 276 277 @Override restartBatchedScan()278 public void restartBatchedScan() { 279 Log.w(TAG, "restartBatchedScan() is not supported"); 280 } 281 handleScanTimeout()282 private void handleScanTimeout() { 283 synchronized (mSettingsLock) { 284 Log.e(TAG, "Timed out waiting for scan result from wificond"); 285 reportScanFailure(); 286 mScanTimeoutListener = null; 287 } 288 } 289 290 @Override handleMessage(Message msg)291 public boolean handleMessage(Message msg) { 292 switch(msg.what) { 293 case WifiMonitor.SCAN_FAILED_EVENT: 294 Log.w(TAG, "Scan failed"); 295 cancelScanTimeout(); 296 reportScanFailure(); 297 break; 298 case WifiMonitor.PNO_SCAN_RESULTS_EVENT: 299 pollLatestScanDataForPno(); 300 break; 301 case WifiMonitor.SCAN_RESULTS_EVENT: 302 cancelScanTimeout(); 303 pollLatestScanData(); 304 break; 305 default: 306 // ignore unknown event 307 } 308 return true; 309 } 310 cancelScanTimeout()311 private void cancelScanTimeout() { 312 synchronized (mSettingsLock) { 313 if (mScanTimeoutListener != null) { 314 mAlarmManager.cancel(mScanTimeoutListener); 315 mScanTimeoutListener = null; 316 } 317 } 318 } 319 reportScanFailure()320 private void reportScanFailure() { 321 synchronized (mSettingsLock) { 322 if (mLastScanSettings != null) { 323 if (mLastScanSettings.singleScanEventHandler != null) { 324 mLastScanSettings.singleScanEventHandler 325 .onScanStatus(WifiNative.WIFI_SCAN_FAILED); 326 } 327 mLastScanSettings = null; 328 } 329 } 330 } 331 reportPnoScanFailure()332 private void reportPnoScanFailure() { 333 synchronized (mSettingsLock) { 334 if (mLastPnoScanSettings != null) { 335 if (mLastPnoScanSettings.pnoScanEventHandler != null) { 336 mLastPnoScanSettings.pnoScanEventHandler.onPnoScanFailed(); 337 } 338 // Clean up PNO state, we don't want to continue PNO scanning. 339 mLastPnoScanSettings = null; 340 } 341 } 342 } 343 pollLatestScanDataForPno()344 private void pollLatestScanDataForPno() { 345 synchronized (mSettingsLock) { 346 if (mLastPnoScanSettings == null) { 347 // got a scan before we started scanning or after scan was canceled 348 return; 349 } 350 mNativePnoScanResults = mWifiNative.getPnoScanResults(getIfaceName()); 351 List<ScanResult> hwPnoScanResults = new ArrayList<>(); 352 int numFilteredScanResults = 0; 353 for (int i = 0; i < mNativePnoScanResults.size(); ++i) { 354 ScanResult result = mNativePnoScanResults.get(i).getScanResult(); 355 // nanoseconds -> microseconds 356 if (result.timestamp >= mLastPnoScanSettings.startTimeNanos / 1_000) { 357 hwPnoScanResults.add(result); 358 } else { 359 numFilteredScanResults++; 360 } 361 } 362 363 if (numFilteredScanResults != 0) { 364 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results."); 365 } 366 367 if (mLastPnoScanSettings.pnoScanEventHandler != null) { 368 ScanResult[] pnoScanResultsArray = 369 hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]); 370 mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); 371 } 372 } 373 } 374 375 /** 376 * Return one of the WIFI_BAND_# values that was scanned for in this scan. 377 */ getScannedBandsInternal(ChannelCollection channelCollection)378 private static int getScannedBandsInternal(ChannelCollection channelCollection) { 379 int bandsScanned = WifiScanner.WIFI_BAND_UNSPECIFIED; 380 381 for (@WifiBandIndex int i = 0; i < WifiScanner.WIFI_BAND_COUNT; i++) { 382 if (channelCollection.containsBand(1 << i)) { 383 bandsScanned |= 1 << i; 384 } 385 } 386 return bandsScanned; 387 } 388 pollLatestScanData()389 private void pollLatestScanData() { 390 synchronized (mSettingsLock) { 391 if (mLastScanSettings == null) { 392 // got a scan before we started scanning or after scan was canceled 393 return; 394 } 395 396 mNativeScanResults = mWifiNative.getScanResults(getIfaceName()); 397 List<ScanResult> singleScanResults = new ArrayList<>(); 398 int numFilteredScanResults = 0; 399 for (int i = 0; i < mNativeScanResults.size(); ++i) { 400 ScanResult result = mNativeScanResults.get(i).getScanResult(); 401 // nanoseconds -> microseconds 402 if (result.timestamp >= mLastScanSettings.startTimeNanos / 1_000) { 403 // Allow even not explicitly requested 6Ghz results because they could be found 404 // via 6Ghz RNR. 405 if (mLastScanSettings.singleScanFreqs.containsChannel( 406 result.frequency) || ScanResult.is6GHz(result.frequency)) { 407 singleScanResults.add(result); 408 } else { 409 numFilteredScanResults++; 410 } 411 } else { 412 numFilteredScanResults++; 413 } 414 } 415 if (numFilteredScanResults != 0) { 416 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); 417 } 418 419 if (mLastScanSettings.singleScanEventHandler != null) { 420 if (mLastScanSettings.reportSingleScanFullResults) { 421 for (ScanResult scanResult : singleScanResults) { 422 // ignore buckets scanned since there is only one bucket for a single scan 423 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 424 /* bucketsScanned */ 0); 425 } 426 } 427 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 428 mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0, 429 getScannedBandsInternal(mLastScanSettings.singleScanFreqs), 430 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 431 mLastScanSettings.singleScanEventHandler 432 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 433 } 434 435 mLastScanSettings = null; 436 } 437 } 438 439 440 @Override getLatestBatchedScanResults(boolean flush)441 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 442 return null; 443 } 444 startHwPnoScan(WifiNative.PnoSettings pnoSettings)445 private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { 446 return mWifiNative.startPnoScan(getIfaceName(), pnoSettings); 447 } 448 stopHwPnoScan()449 private void stopHwPnoScan() { 450 mWifiNative.stopPnoScan(getIfaceName()); 451 } 452 453 /** 454 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 455 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 456 * @return true if HW PNO scan is required, false otherwise. 457 */ isHwPnoScanRequired(boolean isConnectedPno)458 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 459 return (!isConnectedPno 460 && mContext.getResources().getBoolean(R.bool.config_wifi_background_scan_support)); 461 } 462 463 @Override setHwPnoList(WifiNative.PnoSettings settings, WifiNative.PnoEventHandler eventHandler)464 public boolean setHwPnoList(WifiNative.PnoSettings settings, 465 WifiNative.PnoEventHandler eventHandler) { 466 synchronized (mSettingsLock) { 467 if (mLastPnoScanSettings != null) { 468 Log.w(TAG, "Already running a PNO scan"); 469 return false; 470 } 471 if (!isHwPnoScanRequired(settings.isConnected)) { 472 return false; 473 } 474 475 mLastPnoScanSettings = new LastPnoScanSettings( 476 mClock.getElapsedSinceBootNanos(), 477 settings.networkList, eventHandler); 478 479 if (!startHwPnoScan(settings)) { 480 Log.e(TAG, "Failed to start PNO scan"); 481 reportPnoScanFailure(); 482 } 483 return true; 484 } 485 } 486 487 @Override resetHwPnoList()488 public boolean resetHwPnoList() { 489 synchronized (mSettingsLock) { 490 if (mLastPnoScanSettings == null) { 491 Log.w(TAG, "No PNO scan running"); 492 return false; 493 } 494 mLastPnoScanSettings = null; 495 // For wificond based PNO, we stop the scan immediately when we reset pno list. 496 stopHwPnoScan(); 497 return true; 498 } 499 } 500 501 @Override isHwPnoSupported(boolean isConnectedPno)502 public boolean isHwPnoSupported(boolean isConnectedPno) { 503 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 504 return isHwPnoScanRequired(isConnectedPno); 505 } 506 507 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)508 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 509 synchronized (mSettingsLock) { 510 long nowMs = mClock.getElapsedSinceBootMillis(); 511 Log.d(TAG, "Latest native scan results nowMs = " + nowMs); 512 pw.println("Latest native scan results:"); 513 if (mNativeScanResults != null) { 514 List<ScanResult> scanResults = mNativeScanResults.stream().map(r -> { 515 return r.getScanResult(); 516 }).collect(Collectors.toList()); 517 ScanResultUtil.dumpScanResults(pw, scanResults, nowMs); 518 } 519 pw.println("Latest native pno scan results:"); 520 if (mNativePnoScanResults != null) { 521 List<ScanResult> pnoScanResults = mNativePnoScanResults.stream().map(r -> { 522 return r.getScanResult(); 523 }).collect(Collectors.toList()); 524 ScanResultUtil.dumpScanResults(pw, pnoScanResults, nowMs); 525 } 526 pw.println("Latest native scan results IEs:"); 527 if (mNativeScanResults != null) { 528 for (ScanDetail detail : mNativeScanResults) { 529 if (detail.getInformationElementRawData() != null) { 530 pw.println(NativeUtil.hexStringFromByteArray( 531 detail.getInformationElementRawData())); 532 } 533 } 534 } 535 pw.println(""); 536 } 537 } 538 539 private static class LastScanSettings { LastScanSettings(long startTimeNanos, boolean reportSingleScanFullResults, ChannelCollection singleScanFreqs, WifiNative.ScanEventHandler singleScanEventHandler)540 LastScanSettings(long startTimeNanos, 541 boolean reportSingleScanFullResults, 542 ChannelCollection singleScanFreqs, 543 WifiNative.ScanEventHandler singleScanEventHandler) { 544 this.startTimeNanos = startTimeNanos; 545 this.reportSingleScanFullResults = reportSingleScanFullResults; 546 this.singleScanFreqs = singleScanFreqs; 547 this.singleScanEventHandler = singleScanEventHandler; 548 } 549 550 public long startTimeNanos; 551 public boolean reportSingleScanFullResults; 552 public ChannelCollection singleScanFreqs; 553 public WifiNative.ScanEventHandler singleScanEventHandler; 554 555 } 556 557 private static class LastPnoScanSettings { LastPnoScanSettings(long startTimeNanos, WifiNative.PnoNetwork[] pnoNetworkList, WifiNative.PnoEventHandler pnoScanEventHandler)558 LastPnoScanSettings(long startTimeNanos, 559 WifiNative.PnoNetwork[] pnoNetworkList, 560 WifiNative.PnoEventHandler pnoScanEventHandler) { 561 this.startTimeNanos = startTimeNanos; 562 this.pnoNetworkList = pnoNetworkList; 563 this.pnoScanEventHandler = pnoScanEventHandler; 564 } 565 566 public long startTimeNanos; 567 public WifiNative.PnoNetwork[] pnoNetworkList; 568 public WifiNative.PnoEventHandler pnoScanEventHandler; 569 570 } 571 572 } 573