WificondScannerImpl.java revision e6b6ab66c49702d3358b4894d28e322ea9534a84
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
17package com.android.server.wifi.scanner;
18
19import android.app.AlarmManager;
20import android.content.Context;
21import android.net.wifi.ScanResult;
22import android.net.wifi.WifiScanner;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
26import android.util.Log;
27
28import com.android.internal.R;
29import com.android.server.wifi.Clock;
30import com.android.server.wifi.ScanDetail;
31import com.android.server.wifi.WifiMonitor;
32import com.android.server.wifi.WifiNative;
33import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
34
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.ArrayDeque;
38import java.util.ArrayList;
39import java.util.Arrays;
40import java.util.Collections;
41import java.util.HashSet;
42import java.util.List;
43import java.util.Set;
44
45/**
46 * Implementation of the WifiScanner HAL API that uses wificond to perform all scans
47 * @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method.
48 */
49public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback {
50    private static final String TAG = "WificondScannerImpl";
51    private static final boolean DBG = false;
52
53    public static final String BACKGROUND_PERIOD_ALARM_TAG = TAG + " Background Scan Period";
54    public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout";
55    // Max number of networks that can be specified to wificond per scan request
56    public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16;
57
58    private static final int SCAN_BUFFER_CAPACITY = 10;
59    private static final int MAX_APS_PER_SCAN = 32;
60    private static final int MAX_SCAN_BUCKETS = 16;
61
62    private final Context mContext;
63    private final WifiNative mWifiNative;
64    private final AlarmManager mAlarmManager;
65    private final Handler mEventHandler;
66    private final ChannelHelper mChannelHelper;
67    private final Clock mClock;
68
69    private final Object mSettingsLock = new Object();
70
71    // Next scan settings to apply when the previous scan completes
72    private WifiNative.ScanSettings mPendingBackgroundScanSettings = null;
73    private WifiNative.ScanEventHandler mPendingBackgroundScanEventHandler = null;
74    private WifiNative.ScanSettings mPendingSingleScanSettings = null;
75    private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null;
76
77    // Active background scan settings/state
78    private WifiNative.ScanSettings mBackgroundScanSettings = null;
79    private WifiNative.ScanEventHandler mBackgroundScanEventHandler = null;
80    private int mNextBackgroundScanPeriod = 0;
81    private int mNextBackgroundScanId = 0;
82    private boolean mBackgroundScanPeriodPending = false;
83    private boolean mBackgroundScanPaused = false;
84    private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY);
85
86    private ArrayList<ScanDetail> mNativeScanResults;
87    private WifiScanner.ScanData mLatestSingleScanResult =
88            new WifiScanner.ScanData(0, 0, new ScanResult[0]);
89
90    // Settings for the currently running scan, null if no scan active
91    private LastScanSettings mLastScanSettings = null;
92
93    // Pno related info.
94    private WifiNative.PnoSettings mPnoSettings = null;
95    private WifiNative.PnoEventHandler mPnoEventHandler;
96    private final boolean mHwPnoScanSupported;
97    private final HwPnoDebouncer mHwPnoDebouncer;
98    private final HwPnoDebouncer.Listener mHwPnoDebouncerListener = new HwPnoDebouncer.Listener() {
99        public void onPnoScanFailed() {
100            Log.e(TAG, "Pno scan failure received");
101            reportPnoScanFailure();
102        }
103    };
104
105    /**
106     * Duration to wait before timing out a scan.
107     *
108     * The expected behavior is that the hardware will return a failed scan if it does not
109     * complete, but timeout just in case it does not.
110     */
111    private static final long SCAN_TIMEOUT_MS = 15000;
112
113    AlarmManager.OnAlarmListener mScanPeriodListener = new AlarmManager.OnAlarmListener() {
114            public void onAlarm() {
115                synchronized (mSettingsLock) {
116                    handleScanPeriod();
117                }
118            }
119        };
120
121    AlarmManager.OnAlarmListener mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
122            public void onAlarm() {
123                synchronized (mSettingsLock) {
124                    handleScanTimeout();
125                }
126            }
127        };
128
129    public WificondScannerImpl(Context context, WifiNative wifiNative,
130                                     WifiMonitor wifiMonitor, ChannelHelper channelHelper,
131                                     Looper looper, Clock clock) {
132        mContext = context;
133        mWifiNative = wifiNative;
134        mChannelHelper = channelHelper;
135        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
136        mEventHandler = new Handler(looper, this);
137        mClock = clock;
138        mHwPnoDebouncer = new HwPnoDebouncer(mWifiNative, mAlarmManager, mEventHandler, mClock);
139
140        // Check if the device supports HW PNO scans.
141        mHwPnoScanSupported = mContext.getResources().getBoolean(
142                R.bool.config_wifi_background_scan_support);
143
144        wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
145                WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
146        wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
147                WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
148        wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
149                WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
150    }
151
152    public WificondScannerImpl(Context context, WifiNative wifiNative,
153                                     WifiMonitor wifiMonitor, Looper looper, Clock clock) {
154        // TODO get channel information from wificond.
155        this(context, wifiNative, wifiMonitor, new NoBandChannelHelper(), looper, clock);
156    }
157
158    @Override
159    public void cleanup() {
160        synchronized (mSettingsLock) {
161            mPendingSingleScanSettings = null;
162            mPendingSingleScanEventHandler = null;
163            stopHwPnoScan();
164            stopBatchedScan();
165            mLastScanSettings = null; // finally clear any active scan
166        }
167    }
168
169    @Override
170    public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) {
171        capabilities.max_scan_cache_size = Integer.MAX_VALUE;
172        capabilities.max_scan_buckets = MAX_SCAN_BUCKETS;
173        capabilities.max_ap_cache_per_scan = MAX_APS_PER_SCAN;
174        capabilities.max_rssi_sample_size = 8;
175        capabilities.max_scan_reporting_threshold = SCAN_BUFFER_CAPACITY;
176        return true;
177    }
178
179    @Override
180    public ChannelHelper getChannelHelper() {
181        return mChannelHelper;
182    }
183
184    @Override
185    public boolean startSingleScan(WifiNative.ScanSettings settings,
186            WifiNative.ScanEventHandler eventHandler) {
187        if (eventHandler == null || settings == null) {
188            Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
189                    + ",eventHandler=" + eventHandler);
190            return false;
191        }
192        if (mPendingSingleScanSettings != null
193                || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
194            Log.w(TAG, "A single scan is already running");
195            return false;
196        }
197        synchronized (mSettingsLock) {
198            mPendingSingleScanSettings = settings;
199            mPendingSingleScanEventHandler = eventHandler;
200            processPendingScans();
201            return true;
202        }
203    }
204
205    @Override
206    public WifiScanner.ScanData getLatestSingleScanResults() {
207        return mLatestSingleScanResult;
208    }
209
210    @Override
211    public boolean startBatchedScan(WifiNative.ScanSettings settings,
212            WifiNative.ScanEventHandler eventHandler) {
213        if (settings == null || eventHandler == null) {
214            Log.w(TAG, "Invalid arguments for startBatched: settings=" + settings
215                    + ",eventHandler=" + eventHandler);
216            return false;
217        }
218
219        if (settings.max_ap_per_scan < 0 || settings.max_ap_per_scan > MAX_APS_PER_SCAN) {
220            return false;
221        }
222        if (settings.num_buckets < 0 || settings.num_buckets > MAX_SCAN_BUCKETS) {
223            return false;
224        }
225        if (settings.report_threshold_num_scans < 0
226                || settings.report_threshold_num_scans > SCAN_BUFFER_CAPACITY) {
227            return false;
228        }
229        if (settings.report_threshold_percent < 0 || settings.report_threshold_percent > 100) {
230            return false;
231        }
232        if (settings.base_period_ms <= 0) {
233            return false;
234        }
235        for (int i = 0; i < settings.num_buckets; ++i) {
236            WifiNative.BucketSettings bucket = settings.buckets[i];
237            if (bucket.period_ms % settings.base_period_ms != 0) {
238                return false;
239            }
240        }
241
242        synchronized (mSettingsLock) {
243            stopBatchedScan();
244            if (DBG) {
245                Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period="
246                        + settings.base_period_ms + " ms");
247            }
248            mPendingBackgroundScanSettings = settings;
249            mPendingBackgroundScanEventHandler = eventHandler;
250            handleScanPeriod(); // Try to start scan immediately
251            return true;
252        }
253    }
254
255    @Override
256    public void stopBatchedScan() {
257        synchronized (mSettingsLock) {
258            if (DBG) Log.d(TAG, "Stopping scan");
259            mBackgroundScanSettings = null;
260            mBackgroundScanEventHandler = null;
261            mPendingBackgroundScanSettings = null;
262            mPendingBackgroundScanEventHandler = null;
263            mBackgroundScanPaused = false;
264            mBackgroundScanPeriodPending = false;
265            unscheduleScansLocked();
266        }
267        processPendingScans();
268    }
269
270    @Override
271    public void pauseBatchedScan() {
272        synchronized (mSettingsLock) {
273            if (DBG) Log.d(TAG, "Pausing scan");
274            // if there isn't a pending scan then make the current scan pending
275            if (mPendingBackgroundScanSettings == null) {
276                mPendingBackgroundScanSettings = mBackgroundScanSettings;
277                mPendingBackgroundScanEventHandler = mBackgroundScanEventHandler;
278            }
279            mBackgroundScanSettings = null;
280            mBackgroundScanEventHandler = null;
281            mBackgroundScanPeriodPending = false;
282            mBackgroundScanPaused = true;
283
284            unscheduleScansLocked();
285
286            WifiScanner.ScanData[] results = getLatestBatchedScanResults(/* flush = */ true);
287            if (mPendingBackgroundScanEventHandler != null) {
288                mPendingBackgroundScanEventHandler.onScanPaused(results);
289            }
290        }
291        processPendingScans();
292    }
293
294    @Override
295    public void restartBatchedScan() {
296        synchronized (mSettingsLock) {
297            if (DBG) Log.d(TAG, "Restarting scan");
298            if (mPendingBackgroundScanEventHandler != null) {
299                mPendingBackgroundScanEventHandler.onScanRestarted();
300            }
301            mBackgroundScanPaused = false;
302            handleScanPeriod();
303        }
304    }
305
306    private void unscheduleScansLocked() {
307        mAlarmManager.cancel(mScanPeriodListener);
308        if (mLastScanSettings != null) {
309            mLastScanSettings.backgroundScanActive = false;
310        }
311    }
312
313    private void handleScanPeriod() {
314        synchronized (mSettingsLock) {
315            mBackgroundScanPeriodPending = true;
316            processPendingScans();
317        }
318    }
319
320    private void handleScanTimeout() {
321        Log.e(TAG, "Timed out waiting for scan result from wificond");
322        reportScanFailure();
323        processPendingScans();
324    }
325
326    private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) {
327        return (mLastScanSettings == null || !Arrays.equals(
328                newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList));
329    }
330
331    private void processPendingScans() {
332        synchronized (mSettingsLock) {
333            // Wait for the active scan result to come back to reschedule other scans,
334            // unless if HW pno scan is running. Hw PNO scans are paused it if there
335            // are other pending scans,
336            if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) {
337                return;
338            }
339
340            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
341            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
342            final LastScanSettings newScanSettings =
343                    new LastScanSettings(mClock.getElapsedSinceBootMillis());
344
345            // Update scan settings if there is a pending scan
346            if (!mBackgroundScanPaused) {
347                if (mPendingBackgroundScanSettings != null) {
348                    mBackgroundScanSettings = mPendingBackgroundScanSettings;
349                    mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler;
350                    mNextBackgroundScanPeriod = 0;
351                    mPendingBackgroundScanSettings = null;
352                    mPendingBackgroundScanEventHandler = null;
353                    mBackgroundScanPeriodPending = true;
354                }
355                if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) {
356                    int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch
357                    for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets;
358                            ++bucket_id) {
359                        WifiNative.BucketSettings bucket =
360                                mBackgroundScanSettings.buckets[bucket_id];
361                        if (mNextBackgroundScanPeriod % (bucket.period_ms
362                                        / mBackgroundScanSettings.base_period_ms) == 0) {
363                            if ((bucket.report_events
364                                            & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) {
365                                reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
366                            }
367                            if ((bucket.report_events
368                                            & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
369                                reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
370                            }
371                            // only no batch if all buckets specify it
372                            if ((bucket.report_events
373                                            & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {
374                                reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH;
375                            }
376
377                            allFreqs.addChannels(bucket);
378                        }
379                    }
380                    if (!allFreqs.isEmpty()) {
381                        newScanSettings.setBackgroundScan(mNextBackgroundScanId++,
382                                mBackgroundScanSettings.max_ap_per_scan, reportEvents,
383                                mBackgroundScanSettings.report_threshold_num_scans,
384                                mBackgroundScanSettings.report_threshold_percent);
385                    }
386                    mNextBackgroundScanPeriod++;
387                    mBackgroundScanPeriodPending = false;
388                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
389                            mClock.getElapsedSinceBootMillis()
390                                    + mBackgroundScanSettings.base_period_ms,
391                            BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler);
392                }
393            }
394
395            if (mPendingSingleScanSettings != null) {
396                boolean reportFullResults = false;
397                ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection();
398                for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) {
399                    WifiNative.BucketSettings bucketSettings =
400                            mPendingSingleScanSettings.buckets[i];
401                    if ((bucketSettings.report_events
402                                    & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
403                        reportFullResults = true;
404                    }
405                    singleScanFreqs.addChannels(bucketSettings);
406                    allFreqs.addChannels(bucketSettings);
407                }
408                newScanSettings.setSingleScan(reportFullResults, singleScanFreqs,
409                        mPendingSingleScanEventHandler);
410
411                WifiNative.HiddenNetwork[] hiddenNetworks =
412                        mPendingSingleScanSettings.hiddenNetworks;
413                if (hiddenNetworks != null) {
414                    int numHiddenNetworks =
415                            Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
416                    for (int i = 0; i < numHiddenNetworks; i++) {
417                        hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid);
418                    }
419                }
420
421                mPendingSingleScanSettings = null;
422                mPendingSingleScanEventHandler = null;
423            }
424
425            if (newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) {
426                boolean success = false;
427                Set<Integer> freqs;
428                if (!allFreqs.isEmpty()) {
429                    pauseHwPnoScan();
430                    freqs = allFreqs.getScanFreqs();
431                    success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
432                    if (!success) {
433                        Log.e(TAG, "Failed to start scan, freqs=" + freqs);
434                    }
435                } else {
436                    // There is a scan request but no available channels could be scanned for.
437                    // We regard it as a scan failure in this case.
438                    Log.e(TAG, "Failed to start scan because there is "
439                            + "no available channel to scan for");
440                }
441                if (success) {
442                    // TODO handle scan timeout
443                    if (DBG) {
444                        Log.d(TAG, "Starting wifi scan for freqs=" + freqs
445                                + ", background=" + newScanSettings.backgroundScanActive
446                                + ", single=" + newScanSettings.singleScanActive);
447                    }
448                    mLastScanSettings = newScanSettings;
449                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
450                            mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
451                            TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
452                } else {
453                    // indicate scan failure async
454                    mEventHandler.post(new Runnable() {
455                            public void run() {
456                                if (newScanSettings.singleScanEventHandler != null) {
457                                    newScanSettings.singleScanEventHandler
458                                            .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
459                                }
460                            }
461                        });
462                    // TODO(b/27769665) background scans should be failed too if scans fail enough
463                }
464            } else if (isHwPnoScanRequired()) {
465                newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
466                boolean status;
467                // If the PNO network list has changed from the previous request, ensure that
468                // we bypass the debounce logic and restart PNO scan.
469                if (isDifferentPnoScanSettings(newScanSettings)) {
470                    status = restartHwPnoScan(mPnoSettings);
471                } else {
472                    status = startHwPnoScan(mPnoSettings);
473                }
474                if (status) {
475                    mLastScanSettings = newScanSettings;
476                } else {
477                    Log.e(TAG, "Failed to start PNO scan");
478                    // indicate scan failure async
479                    mEventHandler.post(new Runnable() {
480                        public void run() {
481                            if (mPnoEventHandler != null) {
482                                mPnoEventHandler.onPnoScanFailed();
483                            }
484                            // Clean up PNO state, we don't want to continue PNO scanning.
485                            mPnoSettings = null;
486                            mPnoEventHandler = null;
487                        }
488                    });
489                }
490            }
491        }
492    }
493
494    @Override
495    public boolean handleMessage(Message msg) {
496        switch(msg.what) {
497            case WifiMonitor.SCAN_FAILED_EVENT:
498                Log.w(TAG, "Scan failed");
499                mAlarmManager.cancel(mScanTimeoutListener);
500                reportScanFailure();
501                processPendingScans();
502                break;
503            case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
504                pollLatestScanDataForPno();
505                processPendingScans();
506                break;
507            case WifiMonitor.SCAN_RESULTS_EVENT:
508                mAlarmManager.cancel(mScanTimeoutListener);
509                pollLatestScanData();
510                processPendingScans();
511                break;
512            default:
513                // ignore unknown event
514        }
515        return true;
516    }
517
518    private void reportScanFailure() {
519        synchronized (mSettingsLock) {
520            if (mLastScanSettings != null) {
521                if (mLastScanSettings.singleScanEventHandler != null) {
522                    mLastScanSettings.singleScanEventHandler
523                            .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
524                }
525                // TODO(b/27769665) background scans should be failed too if scans fail enough
526                mLastScanSettings = null;
527            }
528        }
529    }
530
531    private void reportPnoScanFailure() {
532        synchronized (mSettingsLock) {
533            if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) {
534                if (mLastScanSettings.pnoScanEventHandler != null) {
535                    mLastScanSettings.pnoScanEventHandler.onPnoScanFailed();
536                }
537                // Clean up PNO state, we don't want to continue PNO scanning.
538                mPnoSettings = null;
539                mPnoEventHandler = null;
540                mLastScanSettings = null;
541            }
542        }
543    }
544
545    private void pollLatestScanDataForPno() {
546        synchronized (mSettingsLock) {
547            if (mLastScanSettings == null) {
548                 // got a scan before we started scanning or after scan was canceled
549                return;
550            }
551            mNativeScanResults = mWifiNative.getPnoScanResults();
552            List<ScanResult> hwPnoScanResults = new ArrayList<>();
553            int numFilteredScanResults = 0;
554            for (int i = 0; i < mNativeScanResults.size(); ++i) {
555                ScanResult result = mNativeScanResults.get(i).getScanResult();
556                long timestamp_ms = result.timestamp / 1000; // convert us -> ms
557                if (timestamp_ms > mLastScanSettings.startTime) {
558                    if (mLastScanSettings.hwPnoScanActive) {
559                        hwPnoScanResults.add(result);
560                    }
561                } else {
562                    numFilteredScanResults++;
563                }
564            }
565
566            if (numFilteredScanResults != 0) {
567                Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
568            }
569
570            if (mLastScanSettings.hwPnoScanActive
571                    && mLastScanSettings.pnoScanEventHandler != null) {
572                ScanResult[] pnoScanResultsArray =
573                        hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
574                mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
575            }
576            // On pno scan result event, we are expecting a mLastScanSettings for pno scan.
577            // However, if unlikey mLastScanSettings is for single scan, we need this part
578            // to protect from leaving WifiSingleScanStateMachine in a forever wait state.
579            if (mLastScanSettings.singleScanActive
580                    && mLastScanSettings.singleScanEventHandler != null) {
581                Log.w(TAG, "Polling pno scan result when single scan is active, reporting"
582                        + " single scan failure");
583                mLastScanSettings.singleScanEventHandler
584                        .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
585            }
586            // mLastScanSettings is for either single/batched scan or pno scan.
587            // We can safely set it to null when pno scan finishes.
588            mLastScanSettings = null;
589        }
590    }
591
592    /**
593     * Check if the provided channel collection contains all the channels.
594     */
595    private static boolean isAllChannelsScanned(ChannelCollection channelCollection) {
596        // TODO(b/62253332): Get rid of this hack.
597        // We're treating 2g + 5g and 2g + 5g + dfs as all channels scanned to work around
598        // the lack of a proper cache.
599        return (channelCollection.containsBand(WifiScanner.WIFI_BAND_24_GHZ)
600                && channelCollection.containsBand(WifiScanner.WIFI_BAND_5_GHZ));
601    }
602
603    private void pollLatestScanData() {
604        synchronized (mSettingsLock) {
605            if (mLastScanSettings == null) {
606                 // got a scan before we started scanning or after scan was canceled
607                return;
608            }
609
610            if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
611            mNativeScanResults = mWifiNative.getScanResults();
612            List<ScanResult> singleScanResults = new ArrayList<>();
613            List<ScanResult> backgroundScanResults = new ArrayList<>();
614            int numFilteredScanResults = 0;
615            for (int i = 0; i < mNativeScanResults.size(); ++i) {
616                ScanResult result = mNativeScanResults.get(i).getScanResult();
617                long timestamp_ms = result.timestamp / 1000; // convert us -> ms
618                if (timestamp_ms > mLastScanSettings.startTime) {
619                    if (mLastScanSettings.backgroundScanActive) {
620                        backgroundScanResults.add(result);
621                    }
622                    if (mLastScanSettings.singleScanActive
623                            && mLastScanSettings.singleScanFreqs.containsChannel(
624                                    result.frequency)) {
625                        singleScanResults.add(result);
626                    }
627                } else {
628                    numFilteredScanResults++;
629                }
630            }
631            if (numFilteredScanResults != 0) {
632                Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
633            }
634
635            if (mLastScanSettings.backgroundScanActive) {
636                if (mBackgroundScanEventHandler != null) {
637                    if ((mLastScanSettings.reportEvents
638                                    & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
639                        for (ScanResult scanResult : backgroundScanResults) {
640                            // TODO(b/27506257): Fill in correct bucketsScanned value
641                            mBackgroundScanEventHandler.onFullScanResult(scanResult, 0);
642                        }
643                    }
644                }
645
646                Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR);
647                ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps,
648                            backgroundScanResults.size())];
649                for (int i = 0; i < scanResultsArray.length; ++i) {
650                    scanResultsArray[i] = backgroundScanResults.get(i);
651                }
652
653                if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) {
654                    // TODO(b/27506257): Fill in correct bucketsScanned value
655                    mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0,
656                                    scanResultsArray));
657                }
658
659                if (mBackgroundScanEventHandler != null) {
660                    if ((mLastScanSettings.reportEvents
661                                    & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0
662                            || (mLastScanSettings.reportEvents
663                                    & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0
664                            || (mLastScanSettings.reportEvents
665                                    == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
666                                    && (mBackgroundScanBuffer.size()
667                                            >= (mBackgroundScanBuffer.capacity()
668                                                    * mLastScanSettings.reportPercentThreshold
669                                                    / 100)
670                                            || mBackgroundScanBuffer.size()
671                                            >= mLastScanSettings.reportNumScansThreshold))) {
672                        mBackgroundScanEventHandler
673                                .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
674                    }
675                }
676            }
677
678            if (mLastScanSettings.singleScanActive
679                    && mLastScanSettings.singleScanEventHandler != null) {
680                if (mLastScanSettings.reportSingleScanFullResults) {
681                    for (ScanResult scanResult : singleScanResults) {
682                        // ignore buckets scanned since there is only one bucket for a single scan
683                        mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult,
684                                /* bucketsScanned */ 0);
685                    }
686                }
687                Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
688                mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0,
689                        isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
690                        singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
691                mLastScanSettings.singleScanEventHandler
692                        .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
693            }
694
695            mLastScanSettings = null;
696        }
697    }
698
699
700    @Override
701    public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) {
702        synchronized (mSettingsLock) {
703            WifiScanner.ScanData[] results = mBackgroundScanBuffer.get();
704            if (flush) {
705                mBackgroundScanBuffer.clear();
706            }
707            return results;
708        }
709    }
710
711    private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
712        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
713    }
714
715    private void stopHwPnoScan() {
716        mHwPnoDebouncer.stopPnoScan();
717    }
718
719    private void pauseHwPnoScan() {
720        mHwPnoDebouncer.forceStopPnoScan();
721    }
722
723    private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) {
724        mHwPnoDebouncer.forceStopPnoScan();
725        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
726    }
727
728    /**
729     * Hw Pno Scan is required only for disconnected PNO when the device supports it.
730     * @param isConnectedPno Whether this is connected PNO vs disconnected PNO.
731     * @return true if HW PNO scan is required, false otherwise.
732     */
733    private boolean isHwPnoScanRequired(boolean isConnectedPno) {
734        return (!isConnectedPno & mHwPnoScanSupported);
735    }
736
737    private boolean isHwPnoScanRequired() {
738        if (mPnoSettings == null) return false;
739        return isHwPnoScanRequired(mPnoSettings.isConnected);
740    }
741
742    @Override
743    public boolean setHwPnoList(WifiNative.PnoSettings settings,
744            WifiNative.PnoEventHandler eventHandler) {
745        synchronized (mSettingsLock) {
746            if (mPnoSettings != null) {
747                Log.w(TAG, "Already running a PNO scan");
748                return false;
749            }
750            mPnoEventHandler = eventHandler;
751            mPnoSettings = settings;
752
753            // For wificond based PNO, we start the scan immediately when we set pno list.
754            processPendingScans();
755            return true;
756        }
757    }
758
759    @Override
760    public boolean resetHwPnoList() {
761        synchronized (mSettingsLock) {
762            if (mPnoSettings == null) {
763                Log.w(TAG, "No PNO scan running");
764                return false;
765            }
766            mPnoEventHandler = null;
767            mPnoSettings = null;
768            // For wificond based PNO, we stop the scan immediately when we reset pno list.
769            stopHwPnoScan();
770            return true;
771        }
772    }
773
774    @Override
775    public boolean isHwPnoSupported(boolean isConnectedPno) {
776        // Hw Pno Scan is supported only for disconnected PNO when the device supports it.
777        return isHwPnoScanRequired(isConnectedPno);
778    }
779
780    @Override
781    public boolean shouldScheduleBackgroundScanForHwPno() {
782        return false;
783    }
784
785    @Override
786    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
787        synchronized (mSettingsLock) {
788            pw.println("Latest native scan results:");
789            if (mNativeScanResults != null && mNativeScanResults.size() != 0) {
790                long nowMs = mClock.getElapsedSinceBootMillis();
791                pw.println("    BSSID              Frequency  RSSI  Age(sec)   SSID "
792                        + "                                Flags");
793                for (ScanDetail scanDetail : mNativeScanResults) {
794                    ScanResult r = scanDetail.getScanResult();
795                    long timeStampMs = r.timestamp / 1000;
796                    String age;
797                    if (timeStampMs <= 0) {
798                        age = "___?___";
799                    } else if (nowMs < timeStampMs) {
800                        age = "  0.000";
801                    } else if (timeStampMs < nowMs - 1000000) {
802                        age = ">1000.0";
803                    } else {
804                        age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0);
805                    }
806                    String ssid = r.SSID == null ? "" : r.SSID;
807                    pw.printf("  %17s  %9d  %5d   %7s    %-32s  %s\n",
808                              r.BSSID,
809                              r.frequency,
810                              r.level,
811                              age,
812                              String.format("%1.32s", ssid),
813                              r.capabilities);
814                }
815            }
816        }
817    }
818
819    private static class LastScanSettings {
820        public long startTime;
821
822        LastScanSettings(long startTime) {
823            this.startTime = startTime;
824        }
825
826        // Background settings
827        public boolean backgroundScanActive = false;
828        public int scanId;
829        public int maxAps;
830        public int reportEvents;
831        public int reportNumScansThreshold;
832        public int reportPercentThreshold;
833
834        public void setBackgroundScan(int scanId, int maxAps, int reportEvents,
835                int reportNumScansThreshold, int reportPercentThreshold) {
836            this.backgroundScanActive = true;
837            this.scanId = scanId;
838            this.maxAps = maxAps;
839            this.reportEvents = reportEvents;
840            this.reportNumScansThreshold = reportNumScansThreshold;
841            this.reportPercentThreshold = reportPercentThreshold;
842        }
843
844        // Single scan settings
845        public boolean singleScanActive = false;
846        public boolean reportSingleScanFullResults;
847        public ChannelCollection singleScanFreqs;
848        public WifiNative.ScanEventHandler singleScanEventHandler;
849
850        public void setSingleScan(boolean reportSingleScanFullResults,
851                ChannelCollection singleScanFreqs,
852                WifiNative.ScanEventHandler singleScanEventHandler) {
853            singleScanActive = true;
854            this.reportSingleScanFullResults = reportSingleScanFullResults;
855            this.singleScanFreqs = singleScanFreqs;
856            this.singleScanEventHandler = singleScanEventHandler;
857        }
858
859        public boolean hwPnoScanActive = false;
860        public WifiNative.PnoNetwork[] pnoNetworkList;
861        public WifiNative.PnoEventHandler pnoScanEventHandler;
862
863        public void setHwPnoScan(
864                WifiNative.PnoNetwork[] pnoNetworkList,
865                WifiNative.PnoEventHandler pnoScanEventHandler) {
866            hwPnoScanActive = true;
867            this.pnoNetworkList = pnoNetworkList;
868            this.pnoScanEventHandler = pnoScanEventHandler;
869        }
870    }
871
872
873    private static class ScanBuffer {
874        private final ArrayDeque<WifiScanner.ScanData> mBuffer;
875        private int mCapacity;
876
877        ScanBuffer(int capacity) {
878            mCapacity = capacity;
879            mBuffer = new ArrayDeque<>(mCapacity);
880        }
881
882        public int size() {
883            return mBuffer.size();
884        }
885
886        public int capacity() {
887            return mCapacity;
888        }
889
890        public boolean isFull() {
891            return size() == mCapacity;
892        }
893
894        public void add(WifiScanner.ScanData scanData) {
895            if (isFull()) {
896                mBuffer.pollFirst();
897            }
898            mBuffer.offerLast(scanData);
899        }
900
901        public void clear() {
902            mBuffer.clear();
903        }
904
905        public WifiScanner.ScanData[] get() {
906            return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]);
907        }
908    }
909
910    /**
911     * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO
912     * state too often which is not handled very well by some drivers.
913     * Note: This is not thread safe!
914     */
915    public static class HwPnoDebouncer {
916        public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor";
917        private static final int MINIMUM_PNO_GAP_MS = 5 * 1000;
918
919        private final WifiNative mWifiNative;
920        private final AlarmManager mAlarmManager;
921        private final Handler mEventHandler;
922        private final Clock mClock;
923        private long mLastPnoChangeTimeStamp = -1L;
924        private boolean mExpectedPnoState = false;
925        private boolean mCurrentPnoState = false;;
926        private boolean mWaitForTimer = false;
927        private Listener mListener;
928        private WifiNative.PnoSettings mPnoSettings;
929
930        /**
931         * Interface used to indicate PNO scan notifications.
932         */
933        public interface Listener {
934            /**
935             * Used to indicate a delayed PNO scan request failure.
936             */
937            void onPnoScanFailed();
938        }
939
940        public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager,
941                Handler eventHandler, Clock clock) {
942            mWifiNative = wifiNative;
943            mAlarmManager = alarmManager;
944            mEventHandler = eventHandler;
945            mClock = clock;
946        }
947
948        /**
949         * Enable PNO state in wificond
950         */
951        private boolean startPnoScanInternal() {
952            if (mCurrentPnoState) {
953                if (DBG) Log.d(TAG, "PNO state is already enable");
954                return true;
955            }
956            if (mPnoSettings == null) {
957                Log.e(TAG, "PNO state change to enable failed, no available Pno settings");
958                return false;
959            }
960            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
961            Log.d(TAG, "Remove all networks from supplicant before starting PNO scan");
962            mWifiNative.removeAllNetworks();
963            if (mWifiNative.startPnoScan(mPnoSettings)) {
964                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable");
965                mCurrentPnoState = true;
966                return true;
967            } else {
968                Log.e(TAG, "PNO state change to enable failed");
969                mCurrentPnoState = false;
970            }
971            return false;
972        }
973
974        /**
975         * Disable PNO state in wificond
976         */
977        private boolean stopPnoScanInternal() {
978            if (!mCurrentPnoState) {
979                if (DBG) Log.d(TAG, "PNO state is already disable");
980                return true;
981            }
982            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
983            if (mWifiNative.stopPnoScan()) {
984                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable");
985                mCurrentPnoState = false;
986                return true;
987            } else {
988                Log.e(TAG, "PNO state change to disable failed");
989                mCurrentPnoState = false;
990            }
991            return false;
992        }
993
994        private final AlarmManager.OnAlarmListener mAlarmListener =
995                new AlarmManager.OnAlarmListener() {
996            public void onAlarm() {
997                if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState);
998                if (mExpectedPnoState) {
999                    if (!startPnoScanInternal()) {
1000                        if (mListener != null) {
1001                            mListener.onPnoScanFailed();
1002                        }
1003                    }
1004                } else {
1005                    stopPnoScanInternal();
1006                }
1007                mWaitForTimer = false;
1008            }
1009        };
1010
1011        /**
1012         * Enable/Disable PNO state. This method will debounce PNO scan requests.
1013         * @param enable boolean indicating whether PNO is being enabled or disabled.
1014         */
1015        private boolean setPnoState(boolean enable) {
1016            boolean isSuccess = true;
1017            mExpectedPnoState = enable;
1018            if (!mWaitForTimer) {
1019                long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp;
1020                if (timeDifference >= MINIMUM_PNO_GAP_MS) {
1021                    if (enable) {
1022                        isSuccess = startPnoScanInternal();
1023                    } else {
1024                        isSuccess = stopPnoScanInternal();
1025                    }
1026                } else {
1027                    long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference;
1028                    Log.d(TAG, "Start PNO timer with delay " + alarmTimeout);
1029                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1030                            mClock.getElapsedSinceBootMillis() + alarmTimeout,
1031                            PNO_DEBOUNCER_ALARM_TAG,
1032                            mAlarmListener, mEventHandler);
1033                    mWaitForTimer = true;
1034                }
1035            }
1036            return isSuccess;
1037        }
1038
1039        /**
1040         * Start PNO scan
1041         */
1042        public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) {
1043            if (DBG) Log.d(TAG, "Starting PNO scan");
1044            mListener = listener;
1045            mPnoSettings = pnoSettings;
1046            if (!setPnoState(true)) {
1047                mListener = null;
1048                return false;
1049            }
1050            return true;
1051        }
1052
1053        /**
1054         * Stop PNO scan
1055         */
1056        public void stopPnoScan() {
1057            if (DBG) Log.d(TAG, "Stopping PNO scan");
1058            setPnoState(false);
1059            mListener = null;
1060        }
1061
1062        /**
1063         * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO
1064         * scan immediately.
1065         */
1066        public void forceStopPnoScan() {
1067            if (DBG) Log.d(TAG, "Force stopping Pno scan");
1068            // Cancel the debounce timer and stop PNO scan.
1069            if (mWaitForTimer) {
1070                mAlarmManager.cancel(mAlarmListener);
1071                mWaitForTimer = false;
1072            }
1073            stopPnoScanInternal();
1074        }
1075    }
1076}
1077