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(WifiScanner.REASON_UNSPECIFIED); 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 int scanStatus = WifiScanner.REASON_UNSPECIFIED; 217 Set<Integer> freqs = Collections.emptySet(); 218 if (!allFreqs.isEmpty()) { 219 freqs = allFreqs.getScanFreqs(); 220 scanStatus = mWifiNative.scan( 221 getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet, 222 settings.enable6GhzRnr, settings.vendorIes); 223 if (scanStatus != WifiScanner.REASON_SUCCEEDED) { 224 Log.e(TAG, "Failed to start scan, freqs=" + freqs + " status: " 225 + scanStatus); 226 } 227 } else { 228 // There is a scan request but no available channels could be scanned for. 229 // We regard it as a scan failure in this case. 230 Log.e(TAG, "Failed to start scan because there is no available channel to scan"); 231 } 232 if (scanStatus == WifiScanner.REASON_SUCCEEDED) { 233 if (DBG) { 234 Log.d(TAG, "Starting wifi scan for freqs=" + freqs 235 + " on iface " + getIfaceName()); 236 } 237 238 mScanTimeoutListener = new AlarmManager.OnAlarmListener() { 239 @Override public void onAlarm() { 240 handleScanTimeout(); 241 } 242 }; 243 244 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 245 mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, 246 TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); 247 } else { 248 // indicate scan failure async 249 int finalScanStatus = scanStatus; 250 mEventHandler.post(() -> reportScanFailure(finalScanStatus)); 251 } 252 253 return true; 254 } 255 } 256 257 @Override getLatestSingleScanResults()258 public WifiScanner.ScanData getLatestSingleScanResults() { 259 return mLatestSingleScanResult; 260 } 261 262 @Override startBatchedScan(WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler)263 public boolean startBatchedScan(WifiNative.ScanSettings settings, 264 WifiNative.ScanEventHandler eventHandler) { 265 Log.w(TAG, "startBatchedScan() is not supported"); 266 return false; 267 } 268 269 @Override stopBatchedScan()270 public void stopBatchedScan() { 271 Log.w(TAG, "stopBatchedScan() is not supported"); 272 } 273 274 @Override pauseBatchedScan()275 public void pauseBatchedScan() { 276 Log.w(TAG, "pauseBatchedScan() is not supported"); 277 } 278 279 @Override restartBatchedScan()280 public void restartBatchedScan() { 281 Log.w(TAG, "restartBatchedScan() is not supported"); 282 } 283 handleScanTimeout()284 private void handleScanTimeout() { 285 synchronized (mSettingsLock) { 286 Log.e(TAG, "Timed out waiting for scan result from wificond"); 287 reportScanFailure(WifiScanner.REASON_TIMEOUT); 288 mScanTimeoutListener = null; 289 } 290 } 291 292 @Override handleMessage(Message msg)293 public boolean handleMessage(Message msg) { 294 switch(msg.what) { 295 case WifiMonitor.SCAN_FAILED_EVENT: 296 Log.w(TAG, "Scan failed: error code: " + msg.arg1); 297 cancelScanTimeout(); 298 reportScanFailure(msg.arg1); 299 break; 300 case WifiMonitor.PNO_SCAN_RESULTS_EVENT: 301 pollLatestScanDataForPno(); 302 break; 303 case WifiMonitor.SCAN_RESULTS_EVENT: 304 cancelScanTimeout(); 305 pollLatestScanData(); 306 break; 307 default: 308 // ignore unknown event 309 } 310 return true; 311 } 312 cancelScanTimeout()313 private void cancelScanTimeout() { 314 synchronized (mSettingsLock) { 315 if (mScanTimeoutListener != null) { 316 mAlarmManager.cancel(mScanTimeoutListener); 317 mScanTimeoutListener = null; 318 } 319 } 320 } 321 reportScanFailure(int errorCode)322 private void reportScanFailure(int errorCode) { 323 synchronized (mSettingsLock) { 324 if (mLastScanSettings != null) { 325 if (mLastScanSettings.singleScanEventHandler != null) { 326 mLastScanSettings.singleScanEventHandler 327 .onScanRequestFailed(errorCode); 328 } 329 mLastScanSettings = null; 330 } 331 } 332 } 333 reportPnoScanFailure()334 private void reportPnoScanFailure() { 335 synchronized (mSettingsLock) { 336 if (mLastPnoScanSettings != null) { 337 if (mLastPnoScanSettings.pnoScanEventHandler != null) { 338 mLastPnoScanSettings.pnoScanEventHandler.onPnoScanFailed(); 339 } 340 // Clean up PNO state, we don't want to continue PNO scanning. 341 mLastPnoScanSettings = null; 342 } 343 } 344 } 345 pollLatestScanDataForPno()346 private void pollLatestScanDataForPno() { 347 synchronized (mSettingsLock) { 348 if (mLastPnoScanSettings == null) { 349 // got a scan before we started scanning or after scan was canceled 350 return; 351 } 352 mNativePnoScanResults = mWifiNative.getPnoScanResults(getIfaceName()); 353 List<ScanResult> hwPnoScanResults = new ArrayList<>(); 354 int numFilteredScanResults = 0; 355 for (int i = 0; i < mNativePnoScanResults.size(); ++i) { 356 ScanResult result = mNativePnoScanResults.get(i).getScanResult(); 357 // nanoseconds -> microseconds 358 if (result.timestamp >= mLastPnoScanSettings.startTimeNanos / 1_000) { 359 hwPnoScanResults.add(result); 360 } else { 361 numFilteredScanResults++; 362 } 363 } 364 365 if (numFilteredScanResults != 0) { 366 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results."); 367 } 368 369 if (mLastPnoScanSettings.pnoScanEventHandler != null) { 370 ScanResult[] pnoScanResultsArray = 371 hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]); 372 mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray); 373 } 374 } 375 } 376 377 /** 378 * Return one of the WIFI_BAND_# values that was scanned for in this scan. 379 */ getScannedBandsInternal(ChannelCollection channelCollection)380 private static int getScannedBandsInternal(ChannelCollection channelCollection) { 381 int bandsScanned = WifiScanner.WIFI_BAND_UNSPECIFIED; 382 383 for (@WifiBandIndex int i = 0; i < WifiScanner.WIFI_BAND_COUNT; i++) { 384 if (channelCollection.containsBand(1 << i)) { 385 bandsScanned |= 1 << i; 386 } 387 } 388 return bandsScanned; 389 } 390 pollLatestScanData()391 private void pollLatestScanData() { 392 synchronized (mSettingsLock) { 393 if (mLastScanSettings == null) { 394 // got a scan before we started scanning or after scan was canceled 395 return; 396 } 397 398 mNativeScanResults = mWifiNative.getScanResults(getIfaceName()); 399 List<ScanResult> singleScanResults = new ArrayList<>(); 400 int numFilteredScanResults = 0; 401 for (int i = 0; i < mNativeScanResults.size(); ++i) { 402 ScanResult result = mNativeScanResults.get(i).getScanResult(); 403 // nanoseconds -> microseconds 404 if (result.timestamp >= mLastScanSettings.startTimeNanos / 1_000) { 405 // Allow even not explicitly requested 6Ghz results because they could be found 406 // via 6Ghz RNR. 407 if (mLastScanSettings.singleScanFreqs.containsChannel( 408 result.frequency) || ScanResult.is6GHz(result.frequency)) { 409 singleScanResults.add(result); 410 } else { 411 numFilteredScanResults++; 412 } 413 } else { 414 numFilteredScanResults++; 415 } 416 } 417 if (numFilteredScanResults != 0) { 418 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); 419 } 420 421 if (mLastScanSettings.singleScanEventHandler != null) { 422 if (mLastScanSettings.reportSingleScanFullResults) { 423 for (ScanResult scanResult : singleScanResults) { 424 // ignore buckets scanned since there is only one bucket for a single scan 425 mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult, 426 /* bucketsScanned */ 0); 427 } 428 } 429 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); 430 mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0, 431 getScannedBandsInternal(mLastScanSettings.singleScanFreqs), 432 singleScanResults.toArray(new ScanResult[singleScanResults.size()])); 433 mLastScanSettings.singleScanEventHandler 434 .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); 435 } 436 437 mLastScanSettings = null; 438 } 439 } 440 441 442 @Override getLatestBatchedScanResults(boolean flush)443 public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { 444 return null; 445 } 446 startHwPnoScan(WifiNative.PnoSettings pnoSettings)447 private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { 448 return mWifiNative.startPnoScan(getIfaceName(), pnoSettings); 449 } 450 stopHwPnoScan()451 private void stopHwPnoScan() { 452 mWifiNative.stopPnoScan(getIfaceName()); 453 } 454 455 /** 456 * Hw Pno Scan is required only for disconnected PNO when the device supports it. 457 * @param isConnectedPno Whether this is connected PNO vs disconnected PNO. 458 * @return true if HW PNO scan is required, false otherwise. 459 */ isHwPnoScanRequired(boolean isConnectedPno)460 private boolean isHwPnoScanRequired(boolean isConnectedPno) { 461 return (!isConnectedPno 462 && mContext.getResources().getBoolean(R.bool.config_wifi_background_scan_support)); 463 } 464 465 @Override setHwPnoList(WifiNative.PnoSettings settings, WifiNative.PnoEventHandler eventHandler)466 public boolean setHwPnoList(WifiNative.PnoSettings settings, 467 WifiNative.PnoEventHandler eventHandler) { 468 synchronized (mSettingsLock) { 469 if (mLastPnoScanSettings != null) { 470 Log.w(TAG, "Already running a PNO scan"); 471 return false; 472 } 473 if (!isHwPnoScanRequired(settings.isConnected)) { 474 return false; 475 } 476 477 mLastPnoScanSettings = new LastPnoScanSettings( 478 mClock.getElapsedSinceBootNanos(), 479 settings.networkList, eventHandler); 480 481 if (!startHwPnoScan(settings)) { 482 Log.e(TAG, "Failed to start PNO scan"); 483 reportPnoScanFailure(); 484 } 485 return true; 486 } 487 } 488 489 @Override resetHwPnoList()490 public boolean resetHwPnoList() { 491 synchronized (mSettingsLock) { 492 if (mLastPnoScanSettings == null) { 493 Log.w(TAG, "No PNO scan running"); 494 return false; 495 } 496 mLastPnoScanSettings = null; 497 // For wificond based PNO, we stop the scan immediately when we reset pno list. 498 stopHwPnoScan(); 499 return true; 500 } 501 } 502 503 @Override isHwPnoSupported(boolean isConnectedPno)504 public boolean isHwPnoSupported(boolean isConnectedPno) { 505 // Hw Pno Scan is supported only for disconnected PNO when the device supports it. 506 return isHwPnoScanRequired(isConnectedPno); 507 } 508 509 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)510 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 511 synchronized (mSettingsLock) { 512 long nowMs = mClock.getElapsedSinceBootMillis(); 513 Log.d(TAG, "Latest native scan results nowMs = " + nowMs); 514 pw.println("Latest native scan results:"); 515 if (mNativeScanResults != null) { 516 List<ScanResult> scanResults = mNativeScanResults.stream().map(r -> { 517 return r.getScanResult(); 518 }).collect(Collectors.toList()); 519 ScanResultUtil.dumpScanResults(pw, scanResults, nowMs); 520 } 521 pw.println("Latest native pno scan results:"); 522 if (mNativePnoScanResults != null) { 523 List<ScanResult> pnoScanResults = mNativePnoScanResults.stream().map(r -> { 524 return r.getScanResult(); 525 }).collect(Collectors.toList()); 526 ScanResultUtil.dumpScanResults(pw, pnoScanResults, nowMs); 527 } 528 pw.println("Latest native scan results IEs:"); 529 if (mNativeScanResults != null) { 530 for (ScanDetail detail : mNativeScanResults) { 531 if (detail.getInformationElementRawData() != null) { 532 pw.println(NativeUtil.hexStringFromByteArray( 533 detail.getInformationElementRawData())); 534 } 535 } 536 } 537 pw.println(""); 538 } 539 } 540 541 private static class LastScanSettings { LastScanSettings(long startTimeNanos, boolean reportSingleScanFullResults, ChannelCollection singleScanFreqs, WifiNative.ScanEventHandler singleScanEventHandler)542 LastScanSettings(long startTimeNanos, 543 boolean reportSingleScanFullResults, 544 ChannelCollection singleScanFreqs, 545 WifiNative.ScanEventHandler singleScanEventHandler) { 546 this.startTimeNanos = startTimeNanos; 547 this.reportSingleScanFullResults = reportSingleScanFullResults; 548 this.singleScanFreqs = singleScanFreqs; 549 this.singleScanEventHandler = singleScanEventHandler; 550 } 551 552 public long startTimeNanos; 553 public boolean reportSingleScanFullResults; 554 public ChannelCollection singleScanFreqs; 555 public WifiNative.ScanEventHandler singleScanEventHandler; 556 557 } 558 559 private static class LastPnoScanSettings { LastPnoScanSettings(long startTimeNanos, WifiNative.PnoNetwork[] pnoNetworkList, WifiNative.PnoEventHandler pnoScanEventHandler)560 LastPnoScanSettings(long startTimeNanos, 561 WifiNative.PnoNetwork[] pnoNetworkList, 562 WifiNative.PnoEventHandler pnoScanEventHandler) { 563 this.startTimeNanos = startTimeNanos; 564 this.pnoNetworkList = pnoNetworkList; 565 this.pnoScanEventHandler = pnoScanEventHandler; 566 } 567 568 public long startTimeNanos; 569 public WifiNative.PnoNetwork[] pnoNetworkList; 570 public WifiNative.PnoEventHandler pnoScanEventHandler; 571 572 } 573 574 } 575