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.annotation.NonNull;
     20 import android.net.MacAddress;
     21 import android.net.wifi.IApInterface;
     22 import android.net.wifi.IApInterfaceEventCallback;
     23 import android.net.wifi.IClientInterface;
     24 import android.net.wifi.IPnoScanEvent;
     25 import android.net.wifi.IScanEvent;
     26 import android.net.wifi.IWifiScannerImpl;
     27 import android.net.wifi.IWificond;
     28 import android.net.wifi.ScanResult;
     29 import android.net.wifi.WifiScanner;
     30 import android.net.wifi.WifiSsid;
     31 import android.os.Binder;
     32 import android.os.IBinder;
     33 import android.os.RemoteException;
     34 import android.util.Log;
     35 
     36 import com.android.server.wifi.WifiNative.SoftApListener;
     37 import com.android.server.wifi.hotspot2.NetworkDetail;
     38 import com.android.server.wifi.util.InformationElementUtil;
     39 import com.android.server.wifi.util.NativeUtil;
     40 import com.android.server.wifi.util.ScanResultUtil;
     41 import com.android.server.wifi.wificond.ChannelSettings;
     42 import com.android.server.wifi.wificond.HiddenNetwork;
     43 import com.android.server.wifi.wificond.NativeScanResult;
     44 import com.android.server.wifi.wificond.PnoNetwork;
     45 import com.android.server.wifi.wificond.PnoSettings;
     46 import com.android.server.wifi.wificond.RadioChainInfo;
     47 import com.android.server.wifi.wificond.SingleScanSettings;
     48 
     49 import java.util.ArrayList;
     50 import java.util.HashMap;
     51 import java.util.Map;
     52 import java.util.Set;
     53 
     54 /**
     55  * This class provides methods for WifiNative to send control commands to wificond.
     56  * NOTE: This class should only be used from WifiNative.
     57  */
     58 public class WificondControl implements IBinder.DeathRecipient {
     59     private boolean mVerboseLoggingEnabled = false;
     60 
     61     private static final String TAG = "WificondControl";
     62 
     63     /* Get scan results for a single scan */
     64     public static final int SCAN_TYPE_SINGLE_SCAN = 0;
     65 
     66     /* Get scan results for Pno Scan */
     67     public static final int SCAN_TYPE_PNO_SCAN = 1;
     68 
     69     private WifiInjector mWifiInjector;
     70     private WifiMonitor mWifiMonitor;
     71     private final CarrierNetworkConfig mCarrierNetworkConfig;
     72 
     73     // Cached wificond binder handlers.
     74     private IWificond mWificond;
     75     private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>();
     76     private HashMap<String, IApInterface> mApInterfaces = new HashMap<>();
     77     private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>();
     78     private HashMap<String, IScanEvent> mScanEventHandlers = new HashMap<>();
     79     private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
     80     private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>();
     81     private WifiNative.WificondDeathEventHandler mDeathEventHandler;
     82 
     83     private class ScanEventHandler extends IScanEvent.Stub {
     84         private String mIfaceName;
     85 
     86         ScanEventHandler(@NonNull String ifaceName) {
     87             mIfaceName = ifaceName;
     88         }
     89 
     90         @Override
     91         public void OnScanResultReady() {
     92             Log.d(TAG, "Scan result ready event");
     93             mWifiMonitor.broadcastScanResultEvent(mIfaceName);
     94         }
     95 
     96         @Override
     97         public void OnScanFailed() {
     98             Log.d(TAG, "Scan failed event");
     99             mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
    100         }
    101     }
    102 
    103     WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor,
    104             CarrierNetworkConfig carrierNetworkConfig) {
    105         mWifiInjector = wifiInjector;
    106         mWifiMonitor = wifiMonitor;
    107         mCarrierNetworkConfig = carrierNetworkConfig;
    108     }
    109 
    110     private class PnoScanEventHandler extends IPnoScanEvent.Stub {
    111         private String mIfaceName;
    112 
    113         PnoScanEventHandler(@NonNull String ifaceName) {
    114             mIfaceName = ifaceName;
    115         }
    116 
    117         @Override
    118         public void OnPnoNetworkFound() {
    119             Log.d(TAG, "Pno scan result event");
    120             mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName);
    121             mWifiInjector.getWifiMetrics().incrementPnoFoundNetworkEventCount();
    122         }
    123 
    124         @Override
    125         public void OnPnoScanFailed() {
    126             Log.d(TAG, "Pno Scan failed event");
    127             mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
    128         }
    129 
    130         @Override
    131         public void OnPnoScanOverOffloadStarted() {
    132             Log.d(TAG, "Pno scan over offload started");
    133             mWifiInjector.getWifiMetrics().incrementPnoScanStartedOverOffloadCount();
    134         }
    135 
    136         @Override
    137         public void OnPnoScanOverOffloadFailed(int reason) {
    138             Log.d(TAG, "Pno scan over offload failed");
    139             mWifiInjector.getWifiMetrics().incrementPnoScanFailedOverOffloadCount();
    140         }
    141     }
    142 
    143     /**
    144      * Listener for AP Interface events.
    145      */
    146     private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub {
    147         private SoftApListener mSoftApListener;
    148 
    149         ApInterfaceEventCallback(SoftApListener listener) {
    150             mSoftApListener = listener;
    151         }
    152 
    153         @Override
    154         public void onNumAssociatedStationsChanged(int numStations) {
    155             mSoftApListener.onNumAssociatedStationsChanged(numStations);
    156         }
    157 
    158         @Override
    159         public void onSoftApChannelSwitched(int frequency, int bandwidth) {
    160             mSoftApListener.onSoftApChannelSwitched(frequency, bandwidth);
    161         }
    162     }
    163 
    164     /**
    165      * Called by the binder subsystem upon remote object death.
    166      * Invoke all the register death handlers and clear state.
    167      */
    168     @Override
    169     public void binderDied() {
    170         Log.e(TAG, "Wificond died!");
    171         clearState();
    172         // Invalidate the global wificond handle on death. Will be refreshed
    173         // on the next setup call.
    174         mWificond = null;
    175         if (mDeathEventHandler != null) {
    176             mDeathEventHandler.onDeath();
    177         }
    178     }
    179 
    180     /** Enable or disable verbose logging of WificondControl.
    181      *  @param enable True to enable verbose logging. False to disable verbose logging.
    182      */
    183     public void enableVerboseLogging(boolean enable) {
    184         mVerboseLoggingEnabled = enable;
    185     }
    186 
    187     /**
    188      * Initializes wificond & registers a death notification for wificond.
    189      * This method clears any existing state in wificond daemon.
    190      *
    191      * @return Returns true on success.
    192      */
    193     public boolean initialize(@NonNull WifiNative.WificondDeathEventHandler handler) {
    194         if (mDeathEventHandler != null) {
    195             Log.e(TAG, "Death handler already present");
    196         }
    197         mDeathEventHandler = handler;
    198         tearDownInterfaces();
    199         return true;
    200     }
    201 
    202     /**
    203      * Helper method to retrieve the global wificond handle and register for
    204      * death notifications.
    205      */
    206     private boolean retrieveWificondAndRegisterForDeath() {
    207         if (mWificond != null) {
    208             if (mVerboseLoggingEnabled) {
    209                 Log.d(TAG, "Wificond handle already retrieved");
    210             }
    211             // We already have a wificond handle.
    212             return true;
    213         }
    214         mWificond = mWifiInjector.makeWificond();
    215         if (mWificond == null) {
    216             Log.e(TAG, "Failed to get reference to wificond");
    217             return false;
    218         }
    219         try {
    220             mWificond.asBinder().linkToDeath(this, 0);
    221         } catch (RemoteException e) {
    222             Log.e(TAG, "Failed to register death notification for wificond");
    223             // The remote has already died.
    224             return false;
    225         }
    226         return true;
    227     }
    228 
    229     /**
    230     * Setup interface for client mode via wificond.
    231     * @return An IClientInterface as wificond client interface binder handler.
    232     * Returns null on failure.
    233     */
    234     public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
    235         Log.d(TAG, "Setting up interface for client mode");
    236         if (!retrieveWificondAndRegisterForDeath()) {
    237             return null;
    238         }
    239 
    240         IClientInterface clientInterface = null;
    241         try {
    242             clientInterface = mWificond.createClientInterface(ifaceName);
    243         } catch (RemoteException e1) {
    244             Log.e(TAG, "Failed to get IClientInterface due to remote exception");
    245             return null;
    246         }
    247 
    248         if (clientInterface == null) {
    249             Log.e(TAG, "Could not get IClientInterface instance from wificond");
    250             return null;
    251         }
    252         Binder.allowBlocking(clientInterface.asBinder());
    253 
    254         // Refresh Handlers
    255         mClientInterfaces.put(ifaceName, clientInterface);
    256         try {
    257             IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
    258             if (wificondScanner == null) {
    259                 Log.e(TAG, "Failed to get WificondScannerImpl");
    260                 return null;
    261             }
    262             mWificondScanners.put(ifaceName, wificondScanner);
    263             Binder.allowBlocking(wificondScanner.asBinder());
    264             ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
    265             mScanEventHandlers.put(ifaceName,  scanEventHandler);
    266             wificondScanner.subscribeScanEvents(scanEventHandler);
    267             PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
    268             mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
    269             wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
    270         } catch (RemoteException e) {
    271             Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
    272         }
    273 
    274         return clientInterface;
    275     }
    276 
    277     /**
    278      * Teardown a specific STA interface configured in wificond.
    279      *
    280      * @return Returns true on success.
    281      */
    282     public boolean tearDownClientInterface(@NonNull String ifaceName) {
    283         if (getClientInterface(ifaceName) == null) {
    284             Log.e(TAG, "No valid wificond client interface handler");
    285             return false;
    286         }
    287         try {
    288             IWifiScannerImpl scannerImpl = mWificondScanners.get(ifaceName);
    289             if (scannerImpl != null) {
    290                 scannerImpl.unsubscribeScanEvents();
    291                 scannerImpl.unsubscribePnoScanEvents();
    292             }
    293         } catch (RemoteException e) {
    294             Log.e(TAG, "Failed to unsubscribe wificond scanner due to remote exception");
    295             return false;
    296         }
    297 
    298         boolean success;
    299         try {
    300             success = mWificond.tearDownClientInterface(ifaceName);
    301         } catch (RemoteException e1) {
    302             Log.e(TAG, "Failed to teardown client interface due to remote exception");
    303             return false;
    304         }
    305         if (!success) {
    306             Log.e(TAG, "Failed to teardown client interface");
    307             return false;
    308         }
    309 
    310         mClientInterfaces.remove(ifaceName);
    311         mWificondScanners.remove(ifaceName);
    312         mScanEventHandlers.remove(ifaceName);
    313         mPnoScanEventHandlers.remove(ifaceName);
    314         return true;
    315     }
    316 
    317     /**
    318     * Setup interface for softAp mode via wificond.
    319     * @return An IApInterface as wificond Ap interface binder handler.
    320     * Returns null on failure.
    321     */
    322     public IApInterface setupInterfaceForSoftApMode(@NonNull String ifaceName) {
    323         Log.d(TAG, "Setting up interface for soft ap mode");
    324         if (!retrieveWificondAndRegisterForDeath()) {
    325             return null;
    326         }
    327 
    328         IApInterface apInterface = null;
    329         try {
    330             apInterface = mWificond.createApInterface(ifaceName);
    331         } catch (RemoteException e1) {
    332             Log.e(TAG, "Failed to get IApInterface due to remote exception");
    333             return null;
    334         }
    335 
    336         if (apInterface == null) {
    337             Log.e(TAG, "Could not get IApInterface instance from wificond");
    338             return null;
    339         }
    340         Binder.allowBlocking(apInterface.asBinder());
    341 
    342         // Refresh Handlers
    343         mApInterfaces.put(ifaceName, apInterface);
    344         return apInterface;
    345     }
    346 
    347     /**
    348      * Teardown a specific AP interface configured in wificond.
    349      *
    350      * @return Returns true on success.
    351      */
    352     public boolean tearDownSoftApInterface(@NonNull String ifaceName) {
    353         if (getApInterface(ifaceName) == null) {
    354             Log.e(TAG, "No valid wificond ap interface handler");
    355             return false;
    356         }
    357         boolean success;
    358         try {
    359             success = mWificond.tearDownApInterface(ifaceName);
    360         } catch (RemoteException e1) {
    361             Log.e(TAG, "Failed to teardown AP interface due to remote exception");
    362             return false;
    363         }
    364         if (!success) {
    365             Log.e(TAG, "Failed to teardown AP interface");
    366             return false;
    367         }
    368         mApInterfaces.remove(ifaceName);
    369         mApInterfaceListeners.remove(ifaceName);
    370         return true;
    371     }
    372 
    373     /**
    374     * Teardown all interfaces configured in wificond.
    375     * @return Returns true on success.
    376     */
    377     public boolean tearDownInterfaces() {
    378         Log.d(TAG, "tearing down interfaces in wificond");
    379         // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
    380         // could be used to cleanup before we setup any interfaces.
    381         if (!retrieveWificondAndRegisterForDeath()) {
    382             return false;
    383         }
    384 
    385         try {
    386             for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
    387                 entry.getValue().unsubscribeScanEvents();
    388                 entry.getValue().unsubscribePnoScanEvents();
    389             }
    390             mWificond.tearDownInterfaces();
    391             clearState();
    392             return true;
    393         } catch (RemoteException e) {
    394             Log.e(TAG, "Failed to tear down interfaces due to remote exception");
    395         }
    396 
    397         return false;
    398     }
    399 
    400     /** Helper function to look up the interface handle using name */
    401     private IClientInterface getClientInterface(@NonNull String ifaceName) {
    402         return mClientInterfaces.get(ifaceName);
    403     }
    404 
    405     /**
    406     * Disable wpa_supplicant via wificond.
    407     * @return Returns true on success.
    408     */
    409     public boolean disableSupplicant() {
    410         if (!retrieveWificondAndRegisterForDeath()) {
    411             return false;
    412         }
    413         try {
    414             return mWificond.disableSupplicant();
    415         } catch (RemoteException e) {
    416             Log.e(TAG, "Failed to disable supplicant due to remote exception");
    417         }
    418         return false;
    419     }
    420 
    421     /**
    422     * Enable wpa_supplicant via wificond.
    423     * @return Returns true on success.
    424     */
    425     public boolean enableSupplicant() {
    426         if (!retrieveWificondAndRegisterForDeath()) {
    427             return false;
    428         }
    429         try {
    430             return mWificond.enableSupplicant();
    431         } catch (RemoteException e) {
    432             Log.e(TAG, "Failed to enable supplicant due to remote exception");
    433         }
    434         return false;
    435     }
    436 
    437     /**
    438      * Request signal polling to wificond.
    439      * @param ifaceName Name of the interface.
    440      * Returns an SignalPollResult object.
    441      * Returns null on failure.
    442      */
    443     public WifiNative.SignalPollResult signalPoll(@NonNull String ifaceName) {
    444         IClientInterface iface = getClientInterface(ifaceName);
    445         if (iface == null) {
    446             Log.e(TAG, "No valid wificond client interface handler");
    447             return null;
    448         }
    449 
    450         int[] resultArray;
    451         try {
    452             resultArray = iface.signalPoll();
    453             if (resultArray == null || resultArray.length != 3) {
    454                 Log.e(TAG, "Invalid signal poll result from wificond");
    455                 return null;
    456             }
    457         } catch (RemoteException e) {
    458             Log.e(TAG, "Failed to do signal polling due to remote exception");
    459             return null;
    460         }
    461         WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult();
    462         pollResult.currentRssi = resultArray[0];
    463         pollResult.txBitrate = resultArray[1];
    464         pollResult.associationFrequency = resultArray[2];
    465         return pollResult;
    466     }
    467 
    468     /**
    469      * Fetch TX packet counters on current connection from wificond.
    470      * @param ifaceName Name of the interface.
    471      * Returns an TxPacketCounters object.
    472      * Returns null on failure.
    473      */
    474     public WifiNative.TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
    475         IClientInterface iface = getClientInterface(ifaceName);
    476         if (iface == null) {
    477             Log.e(TAG, "No valid wificond client interface handler");
    478             return null;
    479         }
    480 
    481         int[] resultArray;
    482         try {
    483             resultArray = iface.getPacketCounters();
    484             if (resultArray == null || resultArray.length != 2) {
    485                 Log.e(TAG, "Invalid signal poll result from wificond");
    486                 return null;
    487             }
    488         } catch (RemoteException e) {
    489             Log.e(TAG, "Failed to do signal polling due to remote exception");
    490             return null;
    491         }
    492         WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters();
    493         counters.txSucceeded = resultArray[0];
    494         counters.txFailed = resultArray[1];
    495         return counters;
    496     }
    497 
    498     /** Helper function to look up the scanner impl handle using name */
    499     private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
    500         return mWificondScanners.get(ifaceName);
    501     }
    502 
    503     /**
    504     * Fetch the latest scan result from kernel via wificond.
    505     * @param ifaceName Name of the interface.
    506     * @return Returns an ArrayList of ScanDetail.
    507     * Returns an empty ArrayList on failure.
    508     */
    509     public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
    510         ArrayList<ScanDetail> results = new ArrayList<>();
    511         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    512         if (scannerImpl == null) {
    513             Log.e(TAG, "No valid wificond scanner interface handler");
    514             return results;
    515         }
    516         try {
    517             NativeScanResult[] nativeResults;
    518             if (scanType == SCAN_TYPE_SINGLE_SCAN) {
    519                 nativeResults = scannerImpl.getScanResults();
    520             } else {
    521                 nativeResults = scannerImpl.getPnoScanResults();
    522             }
    523             for (NativeScanResult result : nativeResults) {
    524                 WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
    525                 String bssid;
    526                 try {
    527                     bssid = NativeUtil.macAddressFromByteArray(result.bssid);
    528                 } catch (IllegalArgumentException e) {
    529                     Log.e(TAG, "Illegal argument " + result.bssid, e);
    530                     continue;
    531                 }
    532                 if (bssid == null) {
    533                     Log.e(TAG, "Illegal null bssid");
    534                     continue;
    535                 }
    536                 ScanResult.InformationElement[] ies =
    537                         InformationElementUtil.parseInformationElements(result.infoElement);
    538                 InformationElementUtil.Capabilities capabilities =
    539                         new InformationElementUtil.Capabilities();
    540                 capabilities.from(ies, result.capability);
    541                 String flags = capabilities.generateCapabilitiesString();
    542                 NetworkDetail networkDetail;
    543                 try {
    544                     networkDetail = new NetworkDetail(bssid, ies, null, result.frequency);
    545                 } catch (IllegalArgumentException e) {
    546                     Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e);
    547                     continue;
    548                 }
    549 
    550                 ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
    551                         result.signalMbm / 100, result.frequency, result.tsf, ies, null);
    552                 ScanResult scanResult = scanDetail.getScanResult();
    553                 // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi
    554                 // network and it uses EAP.
    555                 if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult())
    556                         && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) {
    557                     scanResult.isCarrierAp = true;
    558                     scanResult.carrierApEapType =
    559                             mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString());
    560                     scanResult.carrierName =
    561                             mCarrierNetworkConfig.getCarrierName(wifiSsid.toString());
    562                 }
    563                 // Fill up the radio chain info.
    564                 if (result.radioChainInfos != null) {
    565                     scanResult.radioChainInfos =
    566                         new ScanResult.RadioChainInfo[result.radioChainInfos.size()];
    567                     int idx = 0;
    568                     for (RadioChainInfo nativeRadioChainInfo : result.radioChainInfos) {
    569                         scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo();
    570                         scanResult.radioChainInfos[idx].id = nativeRadioChainInfo.chainId;
    571                         scanResult.radioChainInfos[idx].level = nativeRadioChainInfo.level;
    572                         idx++;
    573                     }
    574                 }
    575                 results.add(scanDetail);
    576             }
    577         } catch (RemoteException e1) {
    578             Log.e(TAG, "Failed to create ScanDetail ArrayList");
    579         }
    580         if (mVerboseLoggingEnabled) {
    581             Log.d(TAG, "get " + results.size() + " scan results from wificond");
    582         }
    583 
    584         return results;
    585     }
    586 
    587     /**
    588      * Return scan type for the parcelable {@link SingleScanSettings}
    589      */
    590     private static int getScanType(int scanType) {
    591         switch (scanType) {
    592             case WifiNative.SCAN_TYPE_LOW_LATENCY:
    593                 return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN;
    594             case WifiNative.SCAN_TYPE_LOW_POWER:
    595                 return IWifiScannerImpl.SCAN_TYPE_LOW_POWER;
    596             case WifiNative.SCAN_TYPE_HIGH_ACCURACY:
    597                 return IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY;
    598             default:
    599                 throw new IllegalArgumentException("Invalid scan type " + scanType);
    600         }
    601     }
    602 
    603     /**
    604      * Start a scan using wificond for the given parameters.
    605      * @param ifaceName Name of the interface.
    606      * @param scanType Type of scan to perform.
    607      * @param freqs list of frequencies to scan for, if null scan all supported channels.
    608      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
    609      * @return Returns true on success.
    610      */
    611     public boolean scan(@NonNull String ifaceName,
    612                         int scanType,
    613                         Set<Integer> freqs,
    614                         Set<String> hiddenNetworkSSIDs) {
    615         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    616         if (scannerImpl == null) {
    617             Log.e(TAG, "No valid wificond scanner interface handler");
    618             return false;
    619         }
    620         SingleScanSettings settings = new SingleScanSettings();
    621         try {
    622             settings.scanType = getScanType(scanType);
    623         } catch (IllegalArgumentException e) {
    624             Log.e(TAG, "Invalid scan type ", e);
    625             return false;
    626         }
    627         settings.channelSettings  = new ArrayList<>();
    628         settings.hiddenNetworks  = new ArrayList<>();
    629 
    630         if (freqs != null) {
    631             for (Integer freq : freqs) {
    632                 ChannelSettings channel = new ChannelSettings();
    633                 channel.frequency = freq;
    634                 settings.channelSettings.add(channel);
    635             }
    636         }
    637         if (hiddenNetworkSSIDs != null) {
    638             for (String ssid : hiddenNetworkSSIDs) {
    639                 HiddenNetwork network = new HiddenNetwork();
    640                 try {
    641                     network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
    642                 } catch (IllegalArgumentException e) {
    643                     Log.e(TAG, "Illegal argument " + ssid, e);
    644                     continue;
    645                 }
    646                 settings.hiddenNetworks.add(network);
    647             }
    648         }
    649 
    650         try {
    651             return scannerImpl.scan(settings);
    652         } catch (RemoteException e1) {
    653             Log.e(TAG, "Failed to request scan due to remote exception");
    654         }
    655         return false;
    656     }
    657 
    658     /**
    659      * Start PNO scan.
    660      * @param ifaceName Name of the interface.
    661      * @param pnoSettings Pno scan configuration.
    662      * @return true on success.
    663      */
    664     public boolean startPnoScan(@NonNull String ifaceName, WifiNative.PnoSettings pnoSettings) {
    665         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    666         if (scannerImpl == null) {
    667             Log.e(TAG, "No valid wificond scanner interface handler");
    668             return false;
    669         }
    670         PnoSettings settings = new PnoSettings();
    671         settings.pnoNetworks  = new ArrayList<>();
    672         settings.intervalMs = pnoSettings.periodInMs;
    673         settings.min2gRssi = pnoSettings.min24GHzRssi;
    674         settings.min5gRssi = pnoSettings.min5GHzRssi;
    675         if (pnoSettings.networkList != null) {
    676             for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
    677                 PnoNetwork condNetwork = new PnoNetwork();
    678                 condNetwork.isHidden = (network.flags
    679                         & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
    680                 try {
    681                     condNetwork.ssid =
    682                             NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
    683                 } catch (IllegalArgumentException e) {
    684                     Log.e(TAG, "Illegal argument " + network.ssid, e);
    685                     continue;
    686                 }
    687                 settings.pnoNetworks.add(condNetwork);
    688             }
    689         }
    690 
    691         try {
    692             boolean success = scannerImpl.startPnoScan(settings);
    693             mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount();
    694             if (!success) {
    695                 mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
    696             }
    697             return success;
    698         } catch (RemoteException e1) {
    699             Log.e(TAG, "Failed to start pno scan due to remote exception");
    700         }
    701         return false;
    702     }
    703 
    704     /**
    705      * Stop PNO scan.
    706      * @param ifaceName Name of the interface.
    707      * @return true on success.
    708      */
    709     public boolean stopPnoScan(@NonNull String ifaceName) {
    710         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    711         if (scannerImpl == null) {
    712             Log.e(TAG, "No valid wificond scanner interface handler");
    713             return false;
    714         }
    715         try {
    716             return scannerImpl.stopPnoScan();
    717         } catch (RemoteException e1) {
    718             Log.e(TAG, "Failed to stop pno scan due to remote exception");
    719         }
    720         return false;
    721     }
    722 
    723     /**
    724      * Abort ongoing single scan.
    725      * @param ifaceName Name of the interface.
    726      */
    727     public void abortScan(@NonNull String ifaceName) {
    728         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
    729         if (scannerImpl == null) {
    730             Log.e(TAG, "No valid wificond scanner interface handler");
    731             return;
    732         }
    733         try {
    734             scannerImpl.abortScan();
    735         } catch (RemoteException e1) {
    736             Log.e(TAG, "Failed to request abortScan due to remote exception");
    737         }
    738     }
    739 
    740     /**
    741      * Query the list of valid frequencies for the provided band.
    742      * The result depends on the on the country code that has been set.
    743      *
    744      * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
    745      * The following bands are supported:
    746      * WifiScanner.WIFI_BAND_24_GHZ
    747      * WifiScanner.WIFI_BAND_5_GHZ
    748      * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
    749      * @return frequencies vector of valid frequencies (MHz), or null for error.
    750      * @throws IllegalArgumentException if band is not recognized.
    751      */
    752     public int [] getChannelsForBand(int band) {
    753         if (mWificond == null) {
    754             Log.e(TAG, "No valid wificond scanner interface handler");
    755             return null;
    756         }
    757         try {
    758             switch (band) {
    759                 case WifiScanner.WIFI_BAND_24_GHZ:
    760                     return mWificond.getAvailable2gChannels();
    761                 case WifiScanner.WIFI_BAND_5_GHZ:
    762                     return mWificond.getAvailable5gNonDFSChannels();
    763                 case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
    764                     return mWificond.getAvailableDFSChannels();
    765                 default:
    766                     throw new IllegalArgumentException("unsupported band " + band);
    767             }
    768         } catch (RemoteException e1) {
    769             Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
    770         }
    771         return null;
    772     }
    773 
    774     /** Helper function to look up the interface handle using name */
    775     private IApInterface getApInterface(@NonNull String ifaceName) {
    776         return mApInterfaces.get(ifaceName);
    777     }
    778 
    779     /**
    780      * Start hostapd
    781      * TODO(b/71513606): Move this to a global operation.
    782      *
    783      * @param ifaceName Name of the interface.
    784      * @param listener Callback for AP events.
    785      * @return true on success, false otherwise.
    786      */
    787     public boolean startHostapd(@NonNull String ifaceName,
    788                                SoftApListener listener) {
    789         IApInterface iface = getApInterface(ifaceName);
    790         if (iface == null) {
    791             Log.e(TAG, "No valid ap interface handler");
    792             return false;
    793         }
    794         try {
    795             IApInterfaceEventCallback  callback = new ApInterfaceEventCallback(listener);
    796             mApInterfaceListeners.put(ifaceName, callback);
    797             boolean success = iface.startHostapd(callback);
    798             if (!success) {
    799                 Log.e(TAG, "Failed to start hostapd.");
    800                 return false;
    801             }
    802         } catch (RemoteException e) {
    803             Log.e(TAG, "Exception in starting soft AP: " + e);
    804             return false;
    805         }
    806         return true;
    807     }
    808 
    809     /**
    810      * Stop hostapd
    811      * TODO(b/71513606): Move this to a global operation.
    812      *
    813      * @param ifaceName Name of the interface.
    814      * @return true on success, false otherwise.
    815      */
    816     public boolean stopHostapd(@NonNull String ifaceName) {
    817         IApInterface iface = getApInterface(ifaceName);
    818         if (iface == null) {
    819             Log.e(TAG, "No valid ap interface handler");
    820             return false;
    821         }
    822         try {
    823             boolean success = iface.stopHostapd();
    824             if (!success) {
    825                 Log.e(TAG, "Failed to stop hostapd.");
    826                 return false;
    827             }
    828         } catch (RemoteException e) {
    829             Log.e(TAG, "Exception in stopping soft AP: " + e);
    830             return false;
    831         }
    832         mApInterfaceListeners.remove(ifaceName);
    833         return true;
    834     }
    835 
    836     /**
    837      * Set Mac address on the given interface
    838      * @param interfaceName Name of the interface.
    839      * @param mac Mac address to change into
    840      * @return true on success, false otherwise.
    841      */
    842     public boolean setMacAddress(@NonNull String interfaceName, @NonNull MacAddress mac) {
    843         IClientInterface mClientInterface = getClientInterface(interfaceName);
    844         if (mClientInterface == null) {
    845             Log.e(TAG, "No valid wificond client interface handler");
    846             return false;
    847         }
    848         byte[] macByteArray = mac.toByteArray();
    849 
    850         try {
    851             mClientInterface.setMacAddress(macByteArray);
    852         } catch (RemoteException e) {
    853             Log.e(TAG, "Failed to setMacAddress due to remote exception");
    854             return false;
    855         }
    856         return true;
    857     }
    858 
    859     /**
    860      * Clear all internal handles.
    861      */
    862     private void clearState() {
    863         // Refresh handlers
    864         mClientInterfaces.clear();
    865         mWificondScanners.clear();
    866         mPnoScanEventHandlers.clear();
    867         mScanEventHandlers.clear();
    868         mApInterfaces.clear();
    869         mApInterfaceListeners.clear();
    870     }
    871 }
    872