Home | History | Annotate | Download | only in scanner
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi.scanner;
     18 
     19 import android.app.AlarmManager;
     20 import android.content.Context;
     21 import android.net.wifi.ScanResult;
     22 import android.net.wifi.WifiScanner;
     23 import android.os.Handler;
     24 import android.os.Looper;
     25 import android.os.Message;
     26 import android.util.Log;
     27 
     28 import com.android.internal.R;
     29 import com.android.server.wifi.Clock;
     30 import com.android.server.wifi.ScanDetail;
     31 import com.android.server.wifi.WifiMonitor;
     32 import com.android.server.wifi.WifiNative;
     33 import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
     34 
     35 import java.util.ArrayDeque;
     36 import java.util.ArrayList;
     37 import java.util.Arrays;
     38 import java.util.Collections;
     39 import java.util.HashSet;
     40 import java.util.List;
     41 import 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  */
     47 public 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