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 import com.android.server.wifi.util.ScanResultUtil;
     35 
     36 import java.io.FileDescriptor;
     37 import java.io.PrintWriter;
     38 import java.util.ArrayList;
     39 import java.util.Collections;
     40 import java.util.HashSet;
     41 import java.util.List;
     42 import java.util.Set;
     43 import java.util.stream.Collectors;
     44 
     45 import javax.annotation.concurrent.GuardedBy;
     46 
     47 /**
     48  * Implementation of the WifiScanner HAL API that uses wificond to perform all scans
     49  * @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method.
     50  */
     51 public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback {
     52     private static final String TAG = "WificondScannerImpl";
     53     private static final boolean DBG = false;
     54 
     55     public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout";
     56     // Max number of networks that can be specified to wificond per scan request
     57     public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16;
     58 
     59     private static final int SCAN_BUFFER_CAPACITY = 10;
     60     private static final int MAX_APS_PER_SCAN = 32;
     61     private static final int MAX_SCAN_BUCKETS = 16;
     62 
     63     private final Context mContext;
     64     private final String mIfaceName;
     65     private final WifiNative mWifiNative;
     66     private final WifiMonitor mWifiMonitor;
     67     private final AlarmManager mAlarmManager;
     68     private final Handler mEventHandler;
     69     private final ChannelHelper mChannelHelper;
     70     private final Clock mClock;
     71 
     72     private final Object mSettingsLock = new Object();
     73 
     74     private ArrayList<ScanDetail> mNativeScanResults;
     75     private ArrayList<ScanDetail> mNativePnoScanResults;
     76     private WifiScanner.ScanData mLatestSingleScanResult =
     77             new WifiScanner.ScanData(0, 0, new ScanResult[0]);
     78 
     79     // Settings for the currently running single scan, null if no scan active
     80     private LastScanSettings mLastScanSettings = null;
     81     // Settings for the currently running pno scan, null if no scan active
     82     private LastPnoScanSettings mLastPnoScanSettings = null;
     83 
     84     private final boolean mHwPnoScanSupported;
     85 
     86     /**
     87      * Duration to wait before timing out a scan.
     88      *
     89      * The expected behavior is that the hardware will return a failed scan if it does not
     90      * complete, but timeout just in case it does not.
     91      */
     92     private static final long SCAN_TIMEOUT_MS = 15000;
     93 
     94     @GuardedBy("mSettingsLock")
     95     private AlarmManager.OnAlarmListener mScanTimeoutListener;
     96 
     97     public WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative,
     98                                WifiMonitor wifiMonitor, ChannelHelper channelHelper,
     99                                Looper looper, Clock clock) {
    100         mContext = context;
    101         mIfaceName = ifaceName;
    102         mWifiNative = wifiNative;
    103         mWifiMonitor = wifiMonitor;
    104         mChannelHelper = channelHelper;
    105         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
    106         mEventHandler = new Handler(looper, this);
    107         mClock = clock;
    108 
    109         // Check if the device supports HW PNO scans.
    110         mHwPnoScanSupported = mContext.getResources().getBoolean(
    111                 R.bool.config_wifi_background_scan_support);
    112 
    113         wifiMonitor.registerHandler(mIfaceName,
    114                 WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
    115         wifiMonitor.registerHandler(mIfaceName,
    116                 WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
    117         wifiMonitor.registerHandler(mIfaceName,
    118                 WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
    119     }
    120 
    121     @Override
    122     public void cleanup() {
    123         synchronized (mSettingsLock) {
    124             stopHwPnoScan();
    125             mLastScanSettings = null; // finally clear any active scan
    126             mLastPnoScanSettings = null; // finally clear any active scan
    127             mWifiMonitor.deregisterHandler(mIfaceName,
    128                     WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
    129             mWifiMonitor.deregisterHandler(mIfaceName,
    130                     WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
    131             mWifiMonitor.deregisterHandler(mIfaceName,
    132                     WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
    133         }
    134     }
    135 
    136     @Override
    137     public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) {
    138         capabilities.max_scan_cache_size = Integer.MAX_VALUE;
    139         capabilities.max_scan_buckets = MAX_SCAN_BUCKETS;
    140         capabilities.max_ap_cache_per_scan = MAX_APS_PER_SCAN;
    141         capabilities.max_rssi_sample_size = 8;
    142         capabilities.max_scan_reporting_threshold = SCAN_BUFFER_CAPACITY;
    143         return true;
    144     }
    145 
    146     @Override
    147     public ChannelHelper getChannelHelper() {
    148         return mChannelHelper;
    149     }
    150 
    151     @Override
    152     public boolean startSingleScan(WifiNative.ScanSettings settings,
    153             WifiNative.ScanEventHandler eventHandler) {
    154         if (eventHandler == null || settings == null) {
    155             Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
    156                     + ",eventHandler=" + eventHandler);
    157             return false;
    158         }
    159         synchronized (mSettingsLock) {
    160             if (mLastScanSettings != null) {
    161                 Log.w(TAG, "A single scan is already running");
    162                 return false;
    163             }
    164 
    165             ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
    166             boolean reportFullResults = false;
    167 
    168             for (int i = 0; i < settings.num_buckets; ++i) {
    169                 WifiNative.BucketSettings bucketSettings = settings.buckets[i];
    170                 if ((bucketSettings.report_events
    171                                 & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
    172                     reportFullResults = true;
    173                 }
    174                 allFreqs.addChannels(bucketSettings);
    175             }
    176 
    177             Set<String> hiddenNetworkSSIDSet = new HashSet<>();
    178             if (settings.hiddenNetworks != null) {
    179                 int numHiddenNetworks =
    180                         Math.min(settings.hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
    181                 for (int i = 0; i < numHiddenNetworks; i++) {
    182                     hiddenNetworkSSIDSet.add(settings.hiddenNetworks[i].ssid);
    183                 }
    184             }
    185             mLastScanSettings = new LastScanSettings(
    186                         mClock.getElapsedSinceBootMillis(),
    187                         reportFullResults, allFreqs, eventHandler);
    188 
    189             boolean success = false;
    190             Set<Integer> freqs;
    191             if (!allFreqs.isEmpty()) {
    192                 freqs = allFreqs.getScanFreqs();
    193                 success = mWifiNative.scan(
    194                         mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
    195                 if (!success) {
    196                     Log.e(TAG, "Failed to start scan, freqs=" + freqs);
    197                 }
    198             } else {
    199                 // There is a scan request but no available channels could be scanned for.
    200                 // We regard it as a scan failure in this case.
    201                 Log.e(TAG, "Failed to start scan because there is no available channel to scan");
    202             }
    203             if (success) {
    204                 if (DBG) {
    205                     Log.d(TAG, "Starting wifi scan for freqs=" + freqs);
    206                 }
    207 
    208                 mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
    209                     @Override public void onAlarm() {
    210                         handleScanTimeout();
    211                     }
    212                 };
    213 
    214                 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    215                         mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
    216                         TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
    217             } else {
    218                 // indicate scan failure async
    219                 mEventHandler.post(new Runnable() {
    220                         @Override public void run() {
    221                             reportScanFailure();
    222                         }
    223                     });
    224             }
    225 
    226             return true;
    227         }
    228     }
    229 
    230     @Override
    231     public WifiScanner.ScanData getLatestSingleScanResults() {
    232         return mLatestSingleScanResult;
    233     }
    234 
    235     @Override
    236     public boolean startBatchedScan(WifiNative.ScanSettings settings,
    237             WifiNative.ScanEventHandler eventHandler) {
    238         Log.w(TAG, "startBatchedScan() is not supported");
    239         return false;
    240     }
    241 
    242     @Override
    243     public void stopBatchedScan() {
    244         Log.w(TAG, "stopBatchedScan() is not supported");
    245     }
    246 
    247     @Override
    248     public void pauseBatchedScan() {
    249         Log.w(TAG, "pauseBatchedScan() is not supported");
    250     }
    251 
    252     @Override
    253     public void restartBatchedScan() {
    254         Log.w(TAG, "restartBatchedScan() is not supported");
    255     }
    256 
    257     private void handleScanTimeout() {
    258         synchronized (mSettingsLock) {
    259             Log.e(TAG, "Timed out waiting for scan result from wificond");
    260             reportScanFailure();
    261             mScanTimeoutListener = null;
    262         }
    263     }
    264 
    265     @Override
    266     public boolean handleMessage(Message msg) {
    267         switch(msg.what) {
    268             case WifiMonitor.SCAN_FAILED_EVENT:
    269                 Log.w(TAG, "Scan failed");
    270                 cancelScanTimeout();
    271                 reportScanFailure();
    272                 break;
    273             case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
    274                 pollLatestScanDataForPno();
    275                 break;
    276             case WifiMonitor.SCAN_RESULTS_EVENT:
    277                 cancelScanTimeout();
    278                 pollLatestScanData();
    279                 break;
    280             default:
    281                 // ignore unknown event
    282         }
    283         return true;
    284     }
    285 
    286     private void cancelScanTimeout() {
    287         synchronized (mSettingsLock) {
    288             if (mScanTimeoutListener != null) {
    289                 mAlarmManager.cancel(mScanTimeoutListener);
    290                 mScanTimeoutListener = null;
    291             }
    292         }
    293     }
    294 
    295     private void reportScanFailure() {
    296         synchronized (mSettingsLock) {
    297             if (mLastScanSettings != null) {
    298                 if (mLastScanSettings.singleScanEventHandler != null) {
    299                     mLastScanSettings.singleScanEventHandler
    300                             .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
    301                 }
    302                 mLastScanSettings = null;
    303             }
    304         }
    305     }
    306 
    307     private void reportPnoScanFailure() {
    308         synchronized (mSettingsLock) {
    309             if (mLastPnoScanSettings != null) {
    310                 if (mLastPnoScanSettings.pnoScanEventHandler != null) {
    311                     mLastPnoScanSettings.pnoScanEventHandler.onPnoScanFailed();
    312                 }
    313                 // Clean up PNO state, we don't want to continue PNO scanning.
    314                 mLastPnoScanSettings = null;
    315             }
    316         }
    317     }
    318 
    319     private void pollLatestScanDataForPno() {
    320         synchronized (mSettingsLock) {
    321             if (mLastPnoScanSettings == null) {
    322                  // got a scan before we started scanning or after scan was canceled
    323                 return;
    324             }
    325             mNativePnoScanResults = mWifiNative.getPnoScanResults(mIfaceName);
    326             List<ScanResult> hwPnoScanResults = new ArrayList<>();
    327             int numFilteredScanResults = 0;
    328             for (int i = 0; i < mNativePnoScanResults.size(); ++i) {
    329                 ScanResult result = mNativePnoScanResults.get(i).getScanResult();
    330                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
    331                 if (timestamp_ms > mLastPnoScanSettings.startTime) {
    332                     hwPnoScanResults.add(result);
    333                 } else {
    334                     numFilteredScanResults++;
    335                 }
    336             }
    337 
    338             if (numFilteredScanResults != 0) {
    339                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
    340             }
    341 
    342             if (mLastPnoScanSettings.pnoScanEventHandler != null) {
    343                 ScanResult[] pnoScanResultsArray =
    344                         hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
    345                 mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
    346             }
    347         }
    348     }
    349 
    350     /**
    351      * Check if the provided channel collection contains all the channels.
    352      */
    353     private static boolean isAllChannelsScanned(ChannelCollection channelCollection) {
    354         // TODO(b/62253332): Get rid of this hack.
    355         // We're treating 2g + 5g and 2g + 5g + dfs as all channels scanned to work around
    356         // the lack of a proper cache.
    357         return (channelCollection.containsBand(WifiScanner.WIFI_BAND_24_GHZ)
    358                 && channelCollection.containsBand(WifiScanner.WIFI_BAND_5_GHZ));
    359     }
    360 
    361     private void pollLatestScanData() {
    362         synchronized (mSettingsLock) {
    363             if (mLastScanSettings == null) {
    364                  // got a scan before we started scanning or after scan was canceled
    365                 return;
    366             }
    367 
    368             mNativeScanResults = mWifiNative.getScanResults(mIfaceName);
    369             List<ScanResult> singleScanResults = new ArrayList<>();
    370             int numFilteredScanResults = 0;
    371             for (int i = 0; i < mNativeScanResults.size(); ++i) {
    372                 ScanResult result = mNativeScanResults.get(i).getScanResult();
    373                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
    374                 if (timestamp_ms > mLastScanSettings.startTime) {
    375                     if (mLastScanSettings.singleScanFreqs.containsChannel(
    376                                     result.frequency)) {
    377                         singleScanResults.add(result);
    378                     }
    379                 } else {
    380                     numFilteredScanResults++;
    381                 }
    382             }
    383             if (numFilteredScanResults != 0) {
    384                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
    385             }
    386 
    387             if (mLastScanSettings.singleScanEventHandler != null) {
    388                 if (mLastScanSettings.reportSingleScanFullResults) {
    389                     for (ScanResult scanResult : singleScanResults) {
    390                         // ignore buckets scanned since there is only one bucket for a single scan
    391                         mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult,
    392                                 /* bucketsScanned */ 0);
    393                     }
    394                 }
    395                 Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
    396                 mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
    397                         isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
    398                         singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
    399                 mLastScanSettings.singleScanEventHandler
    400                         .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
    401             }
    402 
    403             mLastScanSettings = null;
    404         }
    405     }
    406 
    407 
    408     @Override
    409     public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) {
    410         return null;
    411     }
    412 
    413     private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
    414         return mWifiNative.startPnoScan(mIfaceName, pnoSettings);
    415     }
    416 
    417     private void stopHwPnoScan() {
    418         mWifiNative.stopPnoScan(mIfaceName);
    419     }
    420 
    421     /**
    422      * Hw Pno Scan is required only for disconnected PNO when the device supports it.
    423      * @param isConnectedPno Whether this is connected PNO vs disconnected PNO.
    424      * @return true if HW PNO scan is required, false otherwise.
    425      */
    426     private boolean isHwPnoScanRequired(boolean isConnectedPno) {
    427         return (!isConnectedPno && mHwPnoScanSupported);
    428     }
    429 
    430     @Override
    431     public boolean setHwPnoList(WifiNative.PnoSettings settings,
    432             WifiNative.PnoEventHandler eventHandler) {
    433         synchronized (mSettingsLock) {
    434             if (mLastPnoScanSettings != null) {
    435                 Log.w(TAG, "Already running a PNO scan");
    436                 return false;
    437             }
    438             if (!isHwPnoScanRequired(settings.isConnected)) {
    439                 return false;
    440             }
    441 
    442             if (startHwPnoScan(settings)) {
    443                 mLastPnoScanSettings = new LastPnoScanSettings(
    444                             mClock.getElapsedSinceBootMillis(),
    445                             settings.networkList, eventHandler);
    446 
    447             } else {
    448                 Log.e(TAG, "Failed to start PNO scan");
    449                 reportPnoScanFailure();
    450             }
    451             return true;
    452         }
    453     }
    454 
    455     @Override
    456     public boolean resetHwPnoList() {
    457         synchronized (mSettingsLock) {
    458             if (mLastPnoScanSettings == null) {
    459                 Log.w(TAG, "No PNO scan running");
    460                 return false;
    461             }
    462             mLastPnoScanSettings = null;
    463             // For wificond based PNO, we stop the scan immediately when we reset pno list.
    464             stopHwPnoScan();
    465             return true;
    466         }
    467     }
    468 
    469     @Override
    470     public boolean isHwPnoSupported(boolean isConnectedPno) {
    471         // Hw Pno Scan is supported only for disconnected PNO when the device supports it.
    472         return isHwPnoScanRequired(isConnectedPno);
    473     }
    474 
    475     @Override
    476     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    477         synchronized (mSettingsLock) {
    478             long nowMs = mClock.getElapsedSinceBootMillis();
    479             pw.println("Latest native scan results:");
    480             if (mNativeScanResults != null) {
    481                 List<ScanResult> scanResults = mNativeScanResults.stream().map(r -> {
    482                     return r.getScanResult();
    483                 }).collect(Collectors.toList());
    484                 ScanResultUtil.dumpScanResults(pw, scanResults, nowMs);
    485             }
    486             pw.println("Latest native pno scan results:");
    487             if (mNativePnoScanResults != null) {
    488                 List<ScanResult> pnoScanResults = mNativePnoScanResults.stream().map(r -> {
    489                     return r.getScanResult();
    490                 }).collect(Collectors.toList());
    491                 ScanResultUtil.dumpScanResults(pw, pnoScanResults, nowMs);
    492             }
    493         }
    494     }
    495 
    496     private static class LastScanSettings {
    497         LastScanSettings(long startTime,
    498                 boolean reportSingleScanFullResults,
    499                 ChannelCollection singleScanFreqs,
    500                 WifiNative.ScanEventHandler singleScanEventHandler) {
    501             this.startTime = startTime;
    502             this.reportSingleScanFullResults = reportSingleScanFullResults;
    503             this.singleScanFreqs = singleScanFreqs;
    504             this.singleScanEventHandler = singleScanEventHandler;
    505         }
    506 
    507         public long startTime;
    508         public boolean reportSingleScanFullResults;
    509         public ChannelCollection singleScanFreqs;
    510         public WifiNative.ScanEventHandler singleScanEventHandler;
    511 
    512     }
    513 
    514     private static class LastPnoScanSettings {
    515         LastPnoScanSettings(long startTime,
    516                 WifiNative.PnoNetwork[] pnoNetworkList,
    517                 WifiNative.PnoEventHandler pnoScanEventHandler) {
    518             this.startTime = startTime;
    519             this.pnoNetworkList = pnoNetworkList;
    520             this.pnoScanEventHandler = pnoScanEventHandler;
    521         }
    522 
    523         public long startTime;
    524         public WifiNative.PnoNetwork[] pnoNetworkList;
    525         public WifiNative.PnoEventHandler pnoScanEventHandler;
    526 
    527     }
    528 
    529 }
    530