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