Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2017 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;
     18 
     19 import android.net.wifi.IApInterface;
     20 import android.net.wifi.IClientInterface;
     21 import android.net.wifi.IPnoScanEvent;
     22 import android.net.wifi.IScanEvent;
     23 import android.net.wifi.IWifiScannerImpl;
     24 import android.net.wifi.IWificond;
     25 import android.net.wifi.ScanResult;
     26 import android.net.wifi.WifiScanner;
     27 import android.net.wifi.WifiSsid;
     28 import android.os.Binder;
     29 import android.os.RemoteException;
     30 import android.util.Log;
     31 
     32 import com.android.server.wifi.hotspot2.NetworkDetail;
     33 import com.android.server.wifi.util.InformationElementUtil;
     34 import com.android.server.wifi.util.NativeUtil;
     35 import com.android.server.wifi.wificond.ChannelSettings;
     36 import com.android.server.wifi.wificond.HiddenNetwork;
     37 import com.android.server.wifi.wificond.NativeScanResult;
     38 import com.android.server.wifi.wificond.PnoNetwork;
     39 import com.android.server.wifi.wificond.PnoSettings;
     40 import com.android.server.wifi.wificond.SingleScanSettings;
     41 
     42 import java.util.ArrayList;
     43 import java.util.Set;
     44 
     45 /**
     46  * This class provides methods for WifiNative to send control commands to wificond.
     47  * NOTE: This class should only be used from WifiNative.
     48  */
     49 public class WificondControl {
     50     private boolean mVerboseLoggingEnabled = false;
     51 
     52     private static final String TAG = "WificondControl";
     53     private WifiInjector mWifiInjector;
     54     private WifiMonitor mWifiMonitor;
     55 
     56     // Cached wificond binder handlers.
     57     private IWificond mWificond;
     58     private IClientInterface mClientInterface;
     59     private IApInterface mApInterface;
     60     private IWifiScannerImpl mWificondScanner;
     61     private IScanEvent mScanEventHandler;
     62     private IPnoScanEvent mPnoScanEventHandler;
     63 
     64     private String mClientInterfaceName;
     65 
     66 
     67     private class ScanEventHandler extends IScanEvent.Stub {
     68         @Override
     69         public void OnScanResultReady() {
     70             Log.d(TAG, "Scan result ready event");
     71             mWifiMonitor.broadcastScanResultEvent(mClientInterfaceName);
     72         }
     73 
     74         @Override
     75         public void OnScanFailed() {
     76             Log.d(TAG, "Scan failed event");
     77             mWifiMonitor.broadcastScanFailedEvent(mClientInterfaceName);
     78         }
     79     }
     80 
     81     WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor) {
     82         mWifiInjector = wifiInjector;
     83         mWifiMonitor = wifiMonitor;
     84     }
     85 
     86     private class PnoScanEventHandler extends IPnoScanEvent.Stub {
     87         @Override
     88         public void OnPnoNetworkFound() {
     89             Log.d(TAG, "Pno scan result event");
     90             mWifiMonitor.broadcastPnoScanResultEvent(mClientInterfaceName);
     91         }
     92 
     93         @Override
     94         public void OnPnoScanFailed() {
     95             Log.d(TAG, "Pno Scan failed event");
     96             // Nothing to do for now.
     97         }
     98     }
     99 
    100     /** Enable or disable verbose logging of WificondControl.
    101      *  @param enable True to enable verbose logging. False to disable verbose logging.
    102      */
    103     public void enableVerboseLogging(boolean enable) {
    104         mVerboseLoggingEnabled = enable;
    105     }
    106 
    107     /**
    108     * Setup driver for client mode via wificond.
    109     * @return An IClientInterface as wificond client interface binder handler.
    110     * Returns null on failure.
    111     */
    112     public IClientInterface setupDriverForClientMode() {
    113         Log.d(TAG, "Setting up driver for client mode");
    114         mWificond = mWifiInjector.makeWificond();
    115         if (mWificond == null) {
    116             Log.e(TAG, "Failed to get reference to wificond");
    117             return null;
    118         }
    119 
    120         IClientInterface clientInterface = null;
    121         try {
    122             clientInterface = mWificond.createClientInterface();
    123         } catch (RemoteException e1) {
    124             Log.e(TAG, "Failed to get IClientInterface due to remote exception");
    125             return null;
    126         }
    127 
    128         if (clientInterface == null) {
    129             Log.e(TAG, "Could not get IClientInterface instance from wificond");
    130             return null;
    131         }
    132         Binder.allowBlocking(clientInterface.asBinder());
    133 
    134         // Refresh Handlers
    135         mClientInterface = clientInterface;
    136         try {
    137             mClientInterfaceName = clientInterface.getInterfaceName();
    138             mWificondScanner = mClientInterface.getWifiScannerImpl();
    139             if (mWificondScanner == null) {
    140                 Log.e(TAG, "Failed to get WificondScannerImpl");
    141                 return null;
    142             }
    143             Binder.allowBlocking(mWificondScanner.asBinder());
    144             mScanEventHandler = new ScanEventHandler();
    145             mWificondScanner.subscribeScanEvents(mScanEventHandler);
    146             mPnoScanEventHandler = new PnoScanEventHandler();
    147             mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);
    148         } catch (RemoteException e) {
    149             Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
    150         }
    151 
    152         return clientInterface;
    153     }
    154 
    155     /**
    156     * Setup driver for softAp mode via wificond.
    157     * @return An IApInterface as wificond Ap interface binder handler.
    158     * Returns null on failure.
    159     */
    160     public IApInterface setupDriverForSoftApMode() {
    161         Log.d(TAG, "Setting up driver for soft ap mode");
    162         mWificond = mWifiInjector.makeWificond();
    163         if (mWificond == null) {
    164             Log.e(TAG, "Failed to get reference to wificond");
    165             return null;
    166         }
    167 
    168         IApInterface apInterface = null;
    169         try {
    170             apInterface = mWificond.createApInterface();
    171         } catch (RemoteException e1) {
    172             Log.e(TAG, "Failed to get IApInterface due to remote exception");
    173             return null;
    174         }
    175 
    176         if (apInterface == null) {
    177             Log.e(TAG, "Could not get IApInterface instance from wificond");
    178             return null;
    179         }
    180         Binder.allowBlocking(apInterface.asBinder());
    181 
    182         // Refresh Handlers
    183         mApInterface = apInterface;
    184 
    185         return apInterface;
    186     }
    187 
    188     /**
    189     * Teardown all interfaces configured in wificond.
    190     * @return Returns true on success.
    191     */
    192     public boolean tearDownInterfaces() {
    193         Log.d(TAG, "tearing down interfaces in wificond");
    194         // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
    195         // could be used to cleanup before we setup any interfaces.
    196         mWificond = mWifiInjector.makeWificond();
    197         if (mWificond == null) {
    198             Log.e(TAG, "Failed to get reference to wificond");
    199             return false;
    200         }
    201 
    202         try {
    203             if (mWificondScanner != null) {
    204                 mWificondScanner.unsubscribeScanEvents();
    205                 mWificondScanner.unsubscribePnoScanEvents();
    206             }
    207             mWificond.tearDownInterfaces();
    208 
    209             // Refresh handlers
    210             mClientInterface = null;
    211             mWificondScanner = null;
    212             mPnoScanEventHandler = null;
    213             mScanEventHandler = null;
    214             mApInterface = null;
    215 
    216             return true;
    217         } catch (RemoteException e) {
    218             Log.e(TAG, "Failed to tear down interfaces due to remote exception");
    219         }
    220 
    221         return false;
    222     }
    223 
    224     /**
    225     * Disable wpa_supplicant via wificond.
    226     * @return Returns true on success.
    227     */
    228     public boolean disableSupplicant() {
    229         if (mClientInterface == null) {
    230             Log.e(TAG, "No valid wificond client interface handler");
    231             return false;
    232         }
    233         try {
    234             return mClientInterface.disableSupplicant();
    235         } catch (RemoteException e) {
    236             Log.e(TAG, "Failed to disable supplicant due to remote exception");
    237         }
    238         return false;
    239     }
    240 
    241     /**
    242     * Enable wpa_supplicant via wificond.
    243     * @return Returns true on success.
    244     */
    245     public boolean enableSupplicant() {
    246         if (mClientInterface == null) {
    247             Log.e(TAG, "No valid wificond client interface handler");
    248             return false;
    249         }
    250 
    251         try {
    252             return mClientInterface.enableSupplicant();
    253         } catch (RemoteException e) {
    254             Log.e(TAG, "Failed to enable supplicant due to remote exception");
    255         }
    256         return false;
    257     }
    258 
    259     /**
    260     * Request signal polling to wificond.
    261     * Returns an SignalPollResult object.
    262     * Returns null on failure.
    263     */
    264     public WifiNative.SignalPollResult signalPoll() {
    265         if (mClientInterface == null) {
    266             Log.e(TAG, "No valid wificond client interface handler");
    267             return null;
    268         }
    269 
    270         int[] resultArray;
    271         try {
    272             resultArray = mClientInterface.signalPoll();
    273             if (resultArray == null || resultArray.length != 3) {
    274                 Log.e(TAG, "Invalid signal poll result from wificond");
    275                 return null;
    276             }
    277         } catch (RemoteException e) {
    278             Log.e(TAG, "Failed to do signal polling due to remote exception");
    279             return null;
    280         }
    281         WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult();
    282         pollResult.currentRssi = resultArray[0];
    283         pollResult.txBitrate = resultArray[1];
    284         pollResult.associationFrequency = resultArray[2];
    285         return pollResult;
    286     }
    287 
    288     /**
    289     * Fetch TX packet counters on current connection from wificond.
    290     * Returns an TxPacketCounters object.
    291     * Returns null on failure.
    292     */
    293     public WifiNative.TxPacketCounters getTxPacketCounters() {
    294         if (mClientInterface == null) {
    295             Log.e(TAG, "No valid wificond client interface handler");
    296             return null;
    297         }
    298 
    299         int[] resultArray;
    300         try {
    301             resultArray = mClientInterface.getPacketCounters();
    302             if (resultArray == null || resultArray.length != 2) {
    303                 Log.e(TAG, "Invalid signal poll result from wificond");
    304                 return null;
    305             }
    306         } catch (RemoteException e) {
    307             Log.e(TAG, "Failed to do signal polling due to remote exception");
    308             return null;
    309         }
    310         WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters();
    311         counters.txSucceeded = resultArray[0];
    312         counters.txFailed = resultArray[1];
    313         return counters;
    314     }
    315 
    316     /**
    317     * Fetch the latest scan result from kernel via wificond.
    318     * @return Returns an ArrayList of ScanDetail.
    319     * Returns an empty ArrayList on failure.
    320     */
    321     public ArrayList<ScanDetail> getScanResults() {
    322         ArrayList<ScanDetail> results = new ArrayList<>();
    323         if (mWificondScanner == null) {
    324             Log.e(TAG, "No valid wificond scanner interface handler");
    325             return results;
    326         }
    327         try {
    328             NativeScanResult[] nativeResults = mWificondScanner.getScanResults();
    329             for (NativeScanResult result : nativeResults) {
    330                 WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
    331                 String bssid;
    332                 try {
    333                     bssid = NativeUtil.macAddressFromByteArray(result.bssid);
    334                 } catch (IllegalArgumentException e) {
    335                     Log.e(TAG, "Illegal argument " + result.bssid, e);
    336                     continue;
    337                 }
    338                 if (bssid == null) {
    339                     Log.e(TAG, "Illegal null bssid");
    340                     continue;
    341                 }
    342                 ScanResult.InformationElement[] ies =
    343                         InformationElementUtil.parseInformationElements(result.infoElement);
    344                 InformationElementUtil.Capabilities capabilities =
    345                         new InformationElementUtil.Capabilities();
    346                 capabilities.from(ies, result.capability);
    347                 String flags = capabilities.generateCapabilitiesString();
    348                 NetworkDetail networkDetail =
    349                         new NetworkDetail(bssid, ies, null, result.frequency);
    350 
    351                 if (!wifiSsid.toString().equals(networkDetail.getTrimmedSSID())) {
    352                     Log.e(TAG, "Inconsistent SSID on BSSID: " + bssid);
    353                     continue;
    354                 }
    355                 ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
    356                         result.signalMbm / 100, result.frequency, result.tsf, ies, null);
    357                 results.add(scanDetail);
    358             }
    359         } catch (RemoteException e1) {
    360             Log.e(TAG, "Failed to create ScanDetail ArrayList");
    361         }
    362         if (mVerboseLoggingEnabled) {
    363             Log.d(TAG, "get " + results.size() + " scan results from wificond");
    364         }
    365 
    366         return results;
    367     }
    368 
    369     /**
    370      * Start a scan using wificond for the given parameters.
    371      * @param freqs list of frequencies to scan for, if null scan all supported channels.
    372      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
    373      * @return Returns true on success.
    374      */
    375     public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
    376         if (mWificondScanner == null) {
    377             Log.e(TAG, "No valid wificond scanner interface handler");
    378             return false;
    379         }
    380         SingleScanSettings settings = new SingleScanSettings();
    381         settings.channelSettings  = new ArrayList<>();
    382         settings.hiddenNetworks  = new ArrayList<>();
    383 
    384         if (freqs != null) {
    385             for (Integer freq : freqs) {
    386                 ChannelSettings channel = new ChannelSettings();
    387                 channel.frequency = freq;
    388                 settings.channelSettings.add(channel);
    389             }
    390         }
    391         if (hiddenNetworkSSIDs != null) {
    392             for (String ssid : hiddenNetworkSSIDs) {
    393                 HiddenNetwork network = new HiddenNetwork();
    394                 try {
    395                     network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
    396                 } catch (IllegalArgumentException e) {
    397                     Log.e(TAG, "Illegal argument " + ssid, e);
    398                     continue;
    399                 }
    400                 settings.hiddenNetworks.add(network);
    401             }
    402         }
    403 
    404         try {
    405             return mWificondScanner.scan(settings);
    406         } catch (RemoteException e1) {
    407             Log.e(TAG, "Failed to request scan due to remote exception");
    408         }
    409         return false;
    410     }
    411 
    412     /**
    413      * Start PNO scan.
    414      * @param pnoSettings Pno scan configuration.
    415      * @return true on success.
    416      */
    417     public boolean startPnoScan(WifiNative.PnoSettings pnoSettings) {
    418         if (mWificondScanner == null) {
    419             Log.e(TAG, "No valid wificond scanner interface handler");
    420             return false;
    421         }
    422         PnoSettings settings = new PnoSettings();
    423         settings.pnoNetworks  = new ArrayList<>();
    424         settings.intervalMs = pnoSettings.periodInMs;
    425         settings.min2gRssi = pnoSettings.min24GHzRssi;
    426         settings.min5gRssi = pnoSettings.min5GHzRssi;
    427         if (pnoSettings.networkList != null) {
    428             for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
    429                 PnoNetwork condNetwork = new PnoNetwork();
    430                 condNetwork.isHidden = (network.flags
    431                         & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
    432                 try {
    433                     condNetwork.ssid =
    434                             NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
    435                 } catch (IllegalArgumentException e) {
    436                     Log.e(TAG, "Illegal argument " + network.ssid, e);
    437                     continue;
    438                 }
    439                 settings.pnoNetworks.add(condNetwork);
    440             }
    441         }
    442 
    443         try {
    444             return mWificondScanner.startPnoScan(settings);
    445         } catch (RemoteException e1) {
    446             Log.e(TAG, "Failed to stop pno scan due to remote exception");
    447         }
    448         return false;
    449     }
    450 
    451     /**
    452      * Stop PNO scan.
    453      * @return true on success.
    454      */
    455     public boolean stopPnoScan() {
    456         if (mWificondScanner == null) {
    457             Log.e(TAG, "No valid wificond scanner interface handler");
    458             return false;
    459         }
    460         try {
    461             return mWificondScanner.stopPnoScan();
    462         } catch (RemoteException e1) {
    463             Log.e(TAG, "Failed to stop pno scan due to remote exception");
    464         }
    465         return false;
    466     }
    467 
    468     /**
    469      * Abort ongoing single scan.
    470      */
    471     public void abortScan() {
    472         if (mWificondScanner == null) {
    473             Log.e(TAG, "No valid wificond scanner interface handler");
    474             return;
    475         }
    476         try {
    477             mWificondScanner.abortScan();
    478         } catch (RemoteException e1) {
    479             Log.e(TAG, "Failed to request abortScan due to remote exception");
    480         }
    481     }
    482 
    483 }
    484