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