• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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