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