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 package com.android.server.wifi;
     17 
     18 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQP3GPPNetwork;
     19 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPDomName;
     20 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPIPAddrAvailability;
     21 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPNAIRealm;
     22 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPRoamingConsortium;
     23 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPVenueName;
     24 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSConnCapability;
     25 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSFriendlyName;
     26 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSOSUProviders;
     27 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics;
     28 
     29 import android.annotation.NonNull;
     30 import android.content.Context;
     31 import android.hardware.wifi.supplicant.V1_0.ISupplicant;
     32 import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
     33 import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
     34 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
     35 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
     36 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
     37 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
     38 import android.hardware.wifi.supplicant.V1_0.IfaceType;
     39 import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
     40 import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
     41 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
     42 import android.hidl.manager.V1_0.IServiceManager;
     43 import android.hidl.manager.V1_0.IServiceNotification;
     44 import android.net.IpConfiguration;
     45 import android.net.wifi.SupplicantState;
     46 import android.net.wifi.WifiConfiguration;
     47 import android.net.wifi.WifiManager;
     48 import android.net.wifi.WifiSsid;
     49 import android.os.HwRemoteBinder;
     50 import android.os.RemoteException;
     51 import android.text.TextUtils;
     52 import android.util.Log;
     53 import android.util.Pair;
     54 import android.util.SparseArray;
     55 
     56 import com.android.server.wifi.hotspot2.AnqpEvent;
     57 import com.android.server.wifi.hotspot2.IconEvent;
     58 import com.android.server.wifi.hotspot2.WnmData;
     59 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
     60 import com.android.server.wifi.hotspot2.anqp.ANQPParser;
     61 import com.android.server.wifi.hotspot2.anqp.Constants;
     62 import com.android.server.wifi.util.NativeUtil;
     63 
     64 import java.io.IOException;
     65 import java.nio.BufferUnderflowException;
     66 import java.nio.ByteBuffer;
     67 import java.nio.ByteOrder;
     68 import java.util.ArrayList;
     69 import java.util.HashMap;
     70 import java.util.List;
     71 import java.util.Map;
     72 import java.util.regex.Matcher;
     73 import java.util.regex.Pattern;
     74 
     75 /**
     76  * Hal calls for bring up/shut down of the supplicant daemon and for
     77  * sending requests to the supplicant daemon
     78  */
     79 public class SupplicantStaIfaceHal {
     80     private static final String TAG = "SupplicantStaIfaceHal";
     81     /**
     82      * Regex pattern for extracting the wps device type bytes.
     83      * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
     84      */
     85     private static final Pattern WPS_DEVICE_TYPE_PATTERN =
     86             Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
     87 
     88     private final Object mLock = new Object();
     89     private boolean mVerboseLoggingEnabled = false;
     90 
     91     // Supplicant HAL interface objects
     92     private IServiceManager mIServiceManager = null;
     93     private ISupplicant mISupplicant;
     94     private ISupplicantStaIface mISupplicantStaIface;
     95     private ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
     96     private final IServiceNotification mServiceNotificationCallback =
     97             new IServiceNotification.Stub() {
     98         public void onRegistration(String fqName, String name, boolean preexisting) {
     99             synchronized (mLock) {
    100                 if (mVerboseLoggingEnabled) {
    101                     Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
    102                             + ", " + name + " preexisting=" + preexisting);
    103                 }
    104                 if (!initSupplicantService() || !initSupplicantStaIface()) {
    105                     Log.e(TAG, "initalizing ISupplicantIfaces failed.");
    106                     supplicantServiceDiedHandler();
    107                 } else {
    108                     Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
    109                 }
    110             }
    111         }
    112     };
    113     private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
    114             cookie -> {
    115                 Log.w(TAG, "IServiceManager died: cookie=" + cookie);
    116                 synchronized (mLock) {
    117                     supplicantServiceDiedHandler();
    118                     mIServiceManager = null; // Will need to register a new ServiceNotification
    119                 }
    120             };
    121     private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
    122             cookie -> {
    123                 Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
    124                 synchronized (mLock) {
    125                     supplicantServiceDiedHandler();
    126                 }
    127             };
    128 
    129     private String mIfaceName;
    130     private SupplicantStaNetworkHal mCurrentNetworkRemoteHandle;
    131     private WifiConfiguration mCurrentNetworkLocalConfig;
    132     private final Context mContext;
    133     private final WifiMonitor mWifiMonitor;
    134 
    135     public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) {
    136         mContext = context;
    137         mWifiMonitor = monitor;
    138         mISupplicantStaIfaceCallback = new SupplicantStaIfaceHalCallback();
    139     }
    140 
    141     /**
    142      * Enable/Disable verbose logging.
    143      *
    144      * @param enable true to enable, false to disable.
    145      */
    146     void enableVerboseLogging(boolean enable) {
    147         mVerboseLoggingEnabled = enable;
    148     }
    149 
    150     private boolean linkToServiceManagerDeath() {
    151         if (mIServiceManager == null) return false;
    152         try {
    153             if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
    154                 Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
    155                 supplicantServiceDiedHandler();
    156                 mIServiceManager = null; // Will need to register a new ServiceNotification
    157                 return false;
    158             }
    159         } catch (RemoteException e) {
    160             Log.e(TAG, "IServiceManager.linkToDeath exception", e);
    161             return false;
    162         }
    163         return true;
    164     }
    165 
    166     /**
    167      * Registers a service notification for the ISupplicant service, which triggers intialization of
    168      * the ISupplicantStaIface
    169      * @return true if the service notification was successfully registered
    170      */
    171     public boolean initialize() {
    172         if (mVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback.");
    173         synchronized (mLock) {
    174             mISupplicant = null;
    175             mISupplicantStaIface = null;
    176             if (mIServiceManager != null) {
    177                 // Already have an IServiceManager and serviceNotification registered, don't
    178                 // don't register another.
    179                 return true;
    180             }
    181             try {
    182                 mIServiceManager = getServiceManagerMockable();
    183                 if (mIServiceManager == null) {
    184                     Log.e(TAG, "Failed to get HIDL Service Manager");
    185                     return false;
    186                 }
    187                 if (!linkToServiceManagerDeath()) {
    188                     return false;
    189                 }
    190                 /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
    191                    exists */
    192                 if (!mIServiceManager.registerForNotifications(
    193                         ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
    194                     Log.e(TAG, "Failed to register for notifications to "
    195                             + ISupplicant.kInterfaceName);
    196                     mIServiceManager = null; // Will need to register a new ServiceNotification
    197                     return false;
    198                 }
    199             } catch (RemoteException e) {
    200                 Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
    201                         + e);
    202                 supplicantServiceDiedHandler();
    203             }
    204             return true;
    205         }
    206     }
    207 
    208     private boolean linkToSupplicantDeath() {
    209         if (mISupplicant == null) return false;
    210         try {
    211             if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
    212                 Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
    213                 supplicantServiceDiedHandler();
    214                 return false;
    215             }
    216         } catch (RemoteException e) {
    217             Log.e(TAG, "ISupplicant.linkToDeath exception", e);
    218             return false;
    219         }
    220         return true;
    221     }
    222 
    223     private boolean initSupplicantService() {
    224         synchronized (mLock) {
    225             try {
    226                 mISupplicant = getSupplicantMockable();
    227             } catch (RemoteException e) {
    228                 Log.e(TAG, "ISupplicant.getService exception: " + e);
    229                 return false;
    230             }
    231             if (mISupplicant == null) {
    232                 Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
    233                 return false;
    234             }
    235             if (!linkToSupplicantDeath()) {
    236                 return false;
    237             }
    238         }
    239         return true;
    240     }
    241 
    242     private boolean linkToSupplicantStaIfaceDeath() {
    243         if (mISupplicantStaIface == null) return false;
    244         try {
    245             if (!mISupplicantStaIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
    246                 Log.wtf(TAG, "Error on linkToDeath on ISupplicantStaIface");
    247                 supplicantServiceDiedHandler();
    248                 return false;
    249             }
    250         } catch (RemoteException e) {
    251             Log.e(TAG, "ISupplicantStaIface.linkToDeath exception", e);
    252             return false;
    253         }
    254         return true;
    255     }
    256 
    257     private int getCurrentNetworkId() {
    258         if (mCurrentNetworkLocalConfig == null) {
    259             return WifiConfiguration.INVALID_NETWORK_ID;
    260         }
    261         return mCurrentNetworkLocalConfig.networkId;
    262     }
    263 
    264     private boolean initSupplicantStaIface() {
    265         synchronized (mLock) {
    266             /** List all supplicant Ifaces */
    267             final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
    268             try {
    269                 mISupplicant.listInterfaces((SupplicantStatus status,
    270                         ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
    271                     if (status.code != SupplicantStatusCode.SUCCESS) {
    272                         Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
    273                         return;
    274                     }
    275                     supplicantIfaces.addAll(ifaces);
    276                 });
    277             } catch (RemoteException e) {
    278                 Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
    279                 return false;
    280             }
    281             if (supplicantIfaces.size() == 0) {
    282                 Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
    283                 return false;
    284             }
    285             Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
    286             Mutable<String> ifaceName = new Mutable<>();
    287             for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
    288                 if (ifaceInfo.type == IfaceType.STA) {
    289                     try {
    290                         mISupplicant.getInterface(ifaceInfo,
    291                                 (SupplicantStatus status, ISupplicantIface iface) -> {
    292                                 if (status.code != SupplicantStatusCode.SUCCESS) {
    293                                     Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
    294                                     return;
    295                                 }
    296                                 supplicantIface.value = iface;
    297                             });
    298                     } catch (RemoteException e) {
    299                         Log.e(TAG, "ISupplicant.getInterface exception: " + e);
    300                         return false;
    301                     }
    302                     ifaceName.value = ifaceInfo.name;
    303                     break;
    304                 }
    305             }
    306             if (supplicantIface.value == null) {
    307                 Log.e(TAG, "initSupplicantStaIface got null iface");
    308                 return false;
    309             }
    310             mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value);
    311             mIfaceName = ifaceName.value;
    312             if (!linkToSupplicantStaIfaceDeath()) {
    313                 return false;
    314             }
    315             if (!registerCallback(mISupplicantStaIfaceCallback)) {
    316                 return false;
    317             }
    318             return true;
    319         }
    320     }
    321 
    322     private void supplicantServiceDiedHandler() {
    323         synchronized (mLock) {
    324             mISupplicant = null;
    325             mISupplicantStaIface = null;
    326             mWifiMonitor.broadcastSupplicantDisconnectionEvent(mIfaceName);
    327         }
    328     }
    329 
    330     /**
    331      * Signals whether Initialization completed successfully.
    332      */
    333     public boolean isInitializationStarted() {
    334         return mIServiceManager != null;
    335     }
    336 
    337     /**
    338      * Signals whether Initialization completed successfully.
    339      */
    340     public boolean isInitializationComplete() {
    341         return mISupplicantStaIface != null;
    342     }
    343 
    344     /**
    345      * Wrapper functions to access static HAL methods, created to be mockable in unit tests
    346      */
    347     protected IServiceManager getServiceManagerMockable() throws RemoteException {
    348         return IServiceManager.getService();
    349     }
    350 
    351     protected ISupplicant getSupplicantMockable() throws RemoteException {
    352         return ISupplicant.getService();
    353     }
    354 
    355     protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
    356         return ISupplicantStaIface.asInterface(iface.asBinder());
    357     }
    358 
    359     /**
    360      * Add a network configuration to wpa_supplicant.
    361      *
    362      * @param config Config corresponding to the network.
    363      * @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
    364      * for the current network.
    365      */
    366     private Pair<SupplicantStaNetworkHal, WifiConfiguration>
    367             addNetworkAndSaveConfig(WifiConfiguration config) {
    368         logi("addSupplicantStaNetwork via HIDL");
    369         if (config == null) {
    370             loge("Cannot add NULL network!");
    371             return null;
    372         }
    373         SupplicantStaNetworkHal network = addNetwork();
    374         if (network == null) {
    375             loge("Failed to add a network!");
    376             return null;
    377         }
    378         boolean saveSuccess = false;
    379         try {
    380             saveSuccess = network.saveWifiConfiguration(config);
    381         } catch (IllegalArgumentException e) {
    382             Log.e(TAG, "Exception while saving config params: " + config, e);
    383         }
    384         if (!saveSuccess) {
    385             loge("Failed to save variables for: " + config.configKey());
    386             if (!removeAllNetworks()) {
    387                 loge("Failed to remove all networks on failure.");
    388             }
    389             return null;
    390         }
    391         return new Pair(network, new WifiConfiguration(config));
    392     }
    393 
    394     /**
    395      * Add the provided network configuration to wpa_supplicant and initiate connection to it.
    396      * This method does the following:
    397      * 1. If |config| is different to the current supplicant network, removes all supplicant
    398      * networks and saves |config|.
    399      * 2. Select the new network in wpa_supplicant.
    400      *
    401      * @param config WifiConfiguration parameters for the provided network.
    402      * @return {@code true} if it succeeds, {@code false} otherwise
    403      */
    404     public boolean connectToNetwork(@NonNull WifiConfiguration config) {
    405         logd("connectToNetwork " + config.configKey());
    406         if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
    407             logd("Network is already saved, will not trigger remove and add operation.");
    408         } else {
    409             mCurrentNetworkRemoteHandle = null;
    410             mCurrentNetworkLocalConfig = null;
    411             if (!removeAllNetworks()) {
    412                 loge("Failed to remove existing networks");
    413                 return false;
    414             }
    415             Pair<SupplicantStaNetworkHal, WifiConfiguration> pair = addNetworkAndSaveConfig(config);
    416             if (pair == null) {
    417                 loge("Failed to add/save network configuration: " + config.configKey());
    418                 return false;
    419             }
    420             mCurrentNetworkRemoteHandle = pair.first;
    421             mCurrentNetworkLocalConfig = pair.second;
    422         }
    423 
    424         if (!mCurrentNetworkRemoteHandle.select()) {
    425             loge("Failed to select network configuration: " + config.configKey());
    426             return false;
    427         }
    428         return true;
    429     }
    430 
    431     /**
    432      * Initiates roaming to the already configured network in wpa_supplicant. If the network
    433      * configuration provided does not match the already configured network, then this triggers
    434      * a new connection attempt (instead of roam).
    435      * 1. First check if we're attempting to connect to the same network as we currently have
    436      * configured.
    437      * 2. Set the new bssid for the network in wpa_supplicant.
    438      * 3. Trigger reassociate command to wpa_supplicant.
    439      *
    440      * @param config WifiConfiguration parameters for the provided network.
    441      * @return {@code true} if it succeeds, {@code false} otherwise
    442      */
    443     public boolean roamToNetwork(WifiConfiguration config) {
    444         if (getCurrentNetworkId() != config.networkId) {
    445             Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
    446                     + "Current network ID: " + getCurrentNetworkId());
    447             return connectToNetwork(config);
    448         }
    449         String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
    450         logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
    451         if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) {
    452             loge("Failed to set new bssid on network: " + config.configKey());
    453             return false;
    454         }
    455         if (!reassociate()) {
    456             loge("Failed to trigger reassociate");
    457             return false;
    458         }
    459         return true;
    460     }
    461 
    462     /**
    463      * Load all the configured networks from wpa_supplicant.
    464      *
    465      * @param configs       Map of configuration key to configuration objects corresponding to all
    466      *                      the networks.
    467      * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
    468      * @return true if succeeds, false otherwise.
    469      */
    470     public boolean loadNetworks(Map<String, WifiConfiguration> configs,
    471                                 SparseArray<Map<String, String>> networkExtras) {
    472         List<Integer> networkIds = listNetworks();
    473         if (networkIds == null) {
    474             Log.e(TAG, "Failed to list networks");
    475             return false;
    476         }
    477         for (Integer networkId : networkIds) {
    478             SupplicantStaNetworkHal network = getNetwork(networkId);
    479             if (network == null) {
    480                 Log.e(TAG, "Failed to get network with ID: " + networkId);
    481                 return false;
    482             }
    483             WifiConfiguration config = new WifiConfiguration();
    484             Map<String, String> networkExtra = new HashMap<>();
    485             boolean loadSuccess = false;
    486             try {
    487                 loadSuccess = network.loadWifiConfiguration(config, networkExtra);
    488             } catch (IllegalArgumentException e) {
    489                 Log.wtf(TAG, "Exception while loading config params: " + config, e);
    490             }
    491             if (!loadSuccess) {
    492                 Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId
    493                         + ". Skipping...");
    494                 continue;
    495             }
    496             // Set the default IP assignments.
    497             config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
    498             config.setProxySettings(IpConfiguration.ProxySettings.NONE);
    499 
    500             networkExtras.put(networkId, networkExtra);
    501             String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
    502             final WifiConfiguration duplicateConfig = configs.put(configKey, config);
    503             if (duplicateConfig != null) {
    504                 // The network is already known. Overwrite the duplicate entry.
    505                 Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
    506                 removeNetwork(duplicateConfig.networkId);
    507                 networkExtras.remove(duplicateConfig.networkId);
    508             }
    509         }
    510         return true;
    511     }
    512 
    513     /**
    514      * Remove the request |networkId| from supplicant if it's the current network,
    515      * if the current configured network matches |networkId|.
    516      *
    517      * @param networkId network id of the network to be removed from supplicant.
    518      */
    519     public void removeNetworkIfCurrent(int networkId) {
    520         synchronized (mLock) {
    521             if (getCurrentNetworkId() == networkId) {
    522                 // Currently we only save 1 network in supplicant.
    523                 removeAllNetworks();
    524             }
    525         }
    526     }
    527 
    528     /**
    529      * Remove all networks from supplicant
    530      */
    531     public boolean removeAllNetworks() {
    532         synchronized (mLock) {
    533             ArrayList<Integer> networks = listNetworks();
    534             if (networks == null) {
    535                 Log.e(TAG, "removeAllNetworks failed, got null networks");
    536                 return false;
    537             }
    538             for (int id : networks) {
    539                 if (!removeNetwork(id)) {
    540                     Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
    541                     return false;
    542                 }
    543             }
    544         }
    545         // Reset current network info.  Probably not needed once we add support to remove/reset
    546         // current network on receiving disconnection event from supplicant (b/32898136).
    547         mCurrentNetworkLocalConfig = null;
    548         mCurrentNetworkRemoteHandle = null;
    549         return true;
    550     }
    551 
    552     /**
    553      * Set the currently configured network's bssid.
    554      *
    555      * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
    556      * @return true if succeeds, false otherwise.
    557      */
    558     public boolean setCurrentNetworkBssid(String bssidStr) {
    559         if (mCurrentNetworkRemoteHandle == null) return false;
    560         return mCurrentNetworkRemoteHandle.setBssid(bssidStr);
    561     }
    562 
    563     /**
    564      * Get the currently configured network's WPS NFC token.
    565      *
    566      * @return Hex string corresponding to the WPS NFC token.
    567      */
    568     public String getCurrentNetworkWpsNfcConfigurationToken() {
    569         if (mCurrentNetworkRemoteHandle == null) return null;
    570         return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken();
    571     }
    572 
    573     /**
    574      * Get the eap anonymous identity for the currently configured network.
    575      *
    576      * @return anonymous identity string if succeeds, null otherwise.
    577      */
    578     public String getCurrentNetworkEapAnonymousIdentity() {
    579         if (mCurrentNetworkRemoteHandle == null) return null;
    580         return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity();
    581     }
    582 
    583     /**
    584      * Send the eap identity response for the currently configured network.
    585      *
    586      * @param identityStr String to send.
    587      * @return true if succeeds, false otherwise.
    588      */
    589     public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) {
    590         if (mCurrentNetworkRemoteHandle == null) return false;
    591         return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr);
    592     }
    593 
    594     /**
    595      * Send the eap sim gsm auth response for the currently configured network.
    596      *
    597      * @param paramsStr String to send.
    598      * @return true if succeeds, false otherwise.
    599      */
    600     public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) {
    601         if (mCurrentNetworkRemoteHandle == null) return false;
    602         return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
    603     }
    604 
    605     /**
    606      * Send the eap sim gsm auth failure for the currently configured network.
    607      *
    608      * @return true if succeeds, false otherwise.
    609      */
    610     public boolean sendCurrentNetworkEapSimGsmAuthFailure() {
    611         if (mCurrentNetworkRemoteHandle == null) return false;
    612         return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure();
    613     }
    614 
    615     /**
    616      * Send the eap sim umts auth response for the currently configured network.
    617      *
    618      * @param paramsStr String to send.
    619      * @return true if succeeds, false otherwise.
    620      */
    621     public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) {
    622         if (mCurrentNetworkRemoteHandle == null) return false;
    623         return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
    624     }
    625 
    626     /**
    627      * Send the eap sim umts auts response for the currently configured network.
    628      *
    629      * @param paramsStr String to send.
    630      * @return true if succeeds, false otherwise.
    631      */
    632     public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) {
    633         if (mCurrentNetworkRemoteHandle == null) return false;
    634         return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
    635     }
    636 
    637     /**
    638      * Send the eap sim umts auth failure for the currently configured network.
    639      *
    640      * @return true if succeeds, false otherwise.
    641      */
    642     public boolean sendCurrentNetworkEapSimUmtsAuthFailure() {
    643         if (mCurrentNetworkRemoteHandle == null) return false;
    644         return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure();
    645     }
    646 
    647     /**
    648      * Adds a new network.
    649      *
    650      * @return The ISupplicantNetwork object for the new network, or null if the call fails
    651      */
    652     private SupplicantStaNetworkHal addNetwork() {
    653         synchronized (mLock) {
    654             final String methodStr = "addNetwork";
    655             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
    656             Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
    657             try {
    658                 mISupplicantStaIface.addNetwork((SupplicantStatus status,
    659                         ISupplicantNetwork network) -> {
    660                     if (checkStatusAndLogFailure(status, methodStr)) {
    661                         newNetwork.value = network;
    662                     }
    663                 });
    664             } catch (RemoteException e) {
    665                 handleRemoteException(e, methodStr);
    666             }
    667             if (newNetwork.value != null) {
    668                 return getStaNetworkMockable(
    669                         ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
    670             } else {
    671                 return null;
    672             }
    673         }
    674     }
    675 
    676     /**
    677      * Remove network from supplicant with network Id
    678      *
    679      * @return true if request is sent successfully, false otherwise.
    680      */
    681     private boolean removeNetwork(int id) {
    682         synchronized (mLock) {
    683             final String methodStr = "removeNetwork";
    684             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    685             try {
    686                 SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
    687                 return checkStatusAndLogFailure(status, methodStr);
    688             } catch (RemoteException e) {
    689                 handleRemoteException(e, methodStr);
    690                 return false;
    691             }
    692         }
    693     }
    694 
    695     /**
    696      * Use this to mock the creation of SupplicantStaNetworkHal instance.
    697      *
    698      * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
    699      * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
    700      * the call fails
    701      */
    702     protected SupplicantStaNetworkHal getStaNetworkMockable(
    703             ISupplicantStaNetwork iSupplicantStaNetwork) {
    704         SupplicantStaNetworkHal network =
    705                 new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
    706                         mWifiMonitor);
    707         if (network != null) {
    708             network.enableVerboseLogging(mVerboseLoggingEnabled);
    709         }
    710         return network;
    711     }
    712 
    713     /**
    714      * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
    715      * the call fails
    716      */
    717     private SupplicantStaNetworkHal getNetwork(int id) {
    718         synchronized (mLock) {
    719             final String methodStr = "getNetwork";
    720             Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
    721             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
    722             try {
    723                 mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
    724                         ISupplicantNetwork network) -> {
    725                     if (checkStatusAndLogFailure(status, methodStr)) {
    726                         gotNetwork.value = network;
    727                     }
    728                 });
    729             } catch (RemoteException e) {
    730                 handleRemoteException(e, methodStr);
    731             }
    732             if (gotNetwork.value != null) {
    733                 return getStaNetworkMockable(
    734                         ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
    735             } else {
    736                 return null;
    737             }
    738         }
    739     }
    740 
    741     /** See ISupplicantStaNetwork.hal for documentation */
    742     private boolean registerCallback(ISupplicantStaIfaceCallback callback) {
    743         synchronized (mLock) {
    744             final String methodStr = "registerCallback";
    745             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    746             try {
    747                 SupplicantStatus status =  mISupplicantStaIface.registerCallback(callback);
    748                 return checkStatusAndLogFailure(status, methodStr);
    749             } catch (RemoteException e) {
    750                 handleRemoteException(e, methodStr);
    751                 return false;
    752             }
    753         }
    754     }
    755 
    756     /**
    757      * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
    758      * null if the call fails
    759      */
    760     private java.util.ArrayList<Integer> listNetworks() {
    761         synchronized (mLock) {
    762             final String methodStr = "listNetworks";
    763             Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
    764             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
    765             try {
    766                 mISupplicantStaIface.listNetworks((SupplicantStatus status,
    767                         java.util.ArrayList<Integer> networkIds) -> {
    768                     if (checkStatusAndLogFailure(status, methodStr)) {
    769                         networkIdList.value = networkIds;
    770                     }
    771                 });
    772             } catch (RemoteException e) {
    773                 handleRemoteException(e, methodStr);
    774             }
    775             return networkIdList.value;
    776         }
    777     }
    778 
    779     /**
    780      * Set WPS device name.
    781      *
    782      * @param name String to be set.
    783      * @return true if request is sent successfully, false otherwise.
    784      */
    785     public boolean setWpsDeviceName(String name) {
    786         synchronized (mLock) {
    787             final String methodStr = "setWpsDeviceName";
    788             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    789             try {
    790                 SupplicantStatus status = mISupplicantStaIface.setWpsDeviceName(name);
    791                 return checkStatusAndLogFailure(status, methodStr);
    792             } catch (RemoteException e) {
    793                 handleRemoteException(e, methodStr);
    794                 return false;
    795             }
    796         }
    797     }
    798 
    799     /**
    800      * Set WPS device type.
    801      *
    802      * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
    803      * @return true if request is sent successfully, false otherwise.
    804      */
    805     public boolean setWpsDeviceType(String typeStr) {
    806         try {
    807             Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
    808             if (!match.find() || match.groupCount() != 3) {
    809                 Log.e(TAG, "Malformed WPS device type " + typeStr);
    810                 return false;
    811             }
    812             short categ = Short.parseShort(match.group(1));
    813             byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
    814             short subCateg = Short.parseShort(match.group(3));
    815 
    816             byte[] bytes = new byte[8];
    817             ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
    818             byteBuffer.putShort(categ);
    819             byteBuffer.put(oui);
    820             byteBuffer.putShort(subCateg);
    821             return setWpsDeviceType(bytes);
    822         } catch (IllegalArgumentException e) {
    823             Log.e(TAG, "Illegal argument " + typeStr, e);
    824             return false;
    825         }
    826     }
    827 
    828     private boolean setWpsDeviceType(byte[/* 8 */] type) {
    829         synchronized (mLock) {
    830             final String methodStr = "setWpsDeviceType";
    831             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    832             try {
    833                 SupplicantStatus status = mISupplicantStaIface.setWpsDeviceType(type);
    834                 return checkStatusAndLogFailure(status, methodStr);
    835             } catch (RemoteException e) {
    836                 handleRemoteException(e, methodStr);
    837                 return false;
    838             }
    839         }
    840     }
    841 
    842     /**
    843      * Set WPS manufacturer.
    844      *
    845      * @param manufacturer String to be set.
    846      * @return true if request is sent successfully, false otherwise.
    847      */
    848     public boolean setWpsManufacturer(String manufacturer) {
    849         synchronized (mLock) {
    850             final String methodStr = "setWpsManufacturer";
    851             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    852             try {
    853                 SupplicantStatus status = mISupplicantStaIface.setWpsManufacturer(manufacturer);
    854                 return checkStatusAndLogFailure(status, methodStr);
    855             } catch (RemoteException e) {
    856                 handleRemoteException(e, methodStr);
    857                 return false;
    858             }
    859         }
    860     }
    861 
    862     /**
    863      * Set WPS model name.
    864      *
    865      * @param modelName String to be set.
    866      * @return true if request is sent successfully, false otherwise.
    867      */
    868     public boolean setWpsModelName(String modelName) {
    869         synchronized (mLock) {
    870             final String methodStr = "setWpsModelName";
    871             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    872             try {
    873                 SupplicantStatus status = mISupplicantStaIface.setWpsModelName(modelName);
    874                 return checkStatusAndLogFailure(status, methodStr);
    875             } catch (RemoteException e) {
    876                 handleRemoteException(e, methodStr);
    877                 return false;
    878             }
    879         }
    880     }
    881 
    882     /**
    883      * Set WPS model number.
    884      *
    885      * @param modelNumber String to be set.
    886      * @return true if request is sent successfully, false otherwise.
    887      */
    888     public boolean setWpsModelNumber(String modelNumber) {
    889         synchronized (mLock) {
    890             final String methodStr = "setWpsModelNumber";
    891             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    892             try {
    893                 SupplicantStatus status = mISupplicantStaIface.setWpsModelNumber(modelNumber);
    894                 return checkStatusAndLogFailure(status, methodStr);
    895             } catch (RemoteException e) {
    896                 handleRemoteException(e, methodStr);
    897                 return false;
    898             }
    899         }
    900     }
    901 
    902     /**
    903      * Set WPS serial number.
    904      *
    905      * @param serialNumber String to be set.
    906      * @return true if request is sent successfully, false otherwise.
    907      */
    908     public boolean setWpsSerialNumber(String serialNumber) {
    909         synchronized (mLock) {
    910             final String methodStr = "setWpsSerialNumber";
    911             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    912             try {
    913                 SupplicantStatus status = mISupplicantStaIface.setWpsSerialNumber(serialNumber);
    914                 return checkStatusAndLogFailure(status, methodStr);
    915             } catch (RemoteException e) {
    916                 handleRemoteException(e, methodStr);
    917                 return false;
    918             }
    919         }
    920     }
    921 
    922     /**
    923      * Set WPS config methods
    924      *
    925      * @param configMethodsStr List of config methods.
    926      * @return true if request is sent successfully, false otherwise.
    927      */
    928     public boolean setWpsConfigMethods(String configMethodsStr) {
    929         short configMethodsMask = 0;
    930         String[] configMethodsStrArr = configMethodsStr.split("\\s+");
    931         for (int i = 0; i < configMethodsStrArr.length; i++) {
    932             configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
    933         }
    934         return setWpsConfigMethods(configMethodsMask);
    935     }
    936 
    937     private boolean setWpsConfigMethods(short configMethods) {
    938         synchronized (mLock) {
    939             final String methodStr = "setWpsConfigMethods";
    940             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    941             try {
    942                 SupplicantStatus status = mISupplicantStaIface.setWpsConfigMethods(configMethods);
    943                 return checkStatusAndLogFailure(status, methodStr);
    944             } catch (RemoteException e) {
    945                 handleRemoteException(e, methodStr);
    946                 return false;
    947             }
    948         }
    949     }
    950 
    951     /**
    952      * Trigger a reassociation even if the iface is currently connected.
    953      *
    954      * @return true if request is sent successfully, false otherwise.
    955      */
    956     public boolean reassociate() {
    957         synchronized (mLock) {
    958             final String methodStr = "reassociate";
    959             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    960             try {
    961                 SupplicantStatus status = mISupplicantStaIface.reassociate();
    962                 return checkStatusAndLogFailure(status, methodStr);
    963             } catch (RemoteException e) {
    964                 handleRemoteException(e, methodStr);
    965                 return false;
    966             }
    967         }
    968     }
    969 
    970     /**
    971      * Trigger a reconnection if the iface is disconnected.
    972      *
    973      * @return true if request is sent successfully, false otherwise.
    974      */
    975     public boolean reconnect() {
    976         synchronized (mLock) {
    977             final String methodStr = "reconnect";
    978             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    979             try {
    980                 SupplicantStatus status = mISupplicantStaIface.reconnect();
    981                 return checkStatusAndLogFailure(status, methodStr);
    982             } catch (RemoteException e) {
    983                 handleRemoteException(e, methodStr);
    984                 return false;
    985             }
    986         }
    987     }
    988 
    989     /**
    990      * Trigger a disconnection from the currently connected network.
    991      *
    992      * @return true if request is sent successfully, false otherwise.
    993      */
    994     public boolean disconnect() {
    995         synchronized (mLock) {
    996             final String methodStr = "disconnect";
    997             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
    998             try {
    999                 SupplicantStatus status = mISupplicantStaIface.disconnect();
   1000                 return checkStatusAndLogFailure(status, methodStr);
   1001             } catch (RemoteException e) {
   1002                 handleRemoteException(e, methodStr);
   1003                 return false;
   1004             }
   1005         }
   1006     }
   1007 
   1008     /**
   1009      * Enable or disable power save mode.
   1010      *
   1011      * @param enable true to enable, false to disable.
   1012      * @return true if request is sent successfully, false otherwise.
   1013      */
   1014     public boolean setPowerSave(boolean enable) {
   1015         synchronized (mLock) {
   1016             final String methodStr = "setPowerSave";
   1017             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1018             try {
   1019                 SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
   1020                 return checkStatusAndLogFailure(status, methodStr);
   1021             } catch (RemoteException e) {
   1022                 handleRemoteException(e, methodStr);
   1023                 return false;
   1024             }
   1025         }
   1026     }
   1027 
   1028     /**
   1029      * Initiate TDLS discover with the specified AP.
   1030      *
   1031      * @param macAddress MAC Address of the AP.
   1032      * @return true if request is sent successfully, false otherwise.
   1033      */
   1034     public boolean initiateTdlsDiscover(String macAddress) {
   1035         try {
   1036             return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
   1037         } catch (IllegalArgumentException e) {
   1038             Log.e(TAG, "Illegal argument " + macAddress, e);
   1039             return false;
   1040         }
   1041     }
   1042     /** See ISupplicantStaIface.hal for documentation */
   1043     private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
   1044         synchronized (mLock) {
   1045             final String methodStr = "initiateTdlsDiscover";
   1046             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1047             try {
   1048                 SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
   1049                 return checkStatusAndLogFailure(status, methodStr);
   1050             } catch (RemoteException e) {
   1051                 handleRemoteException(e, methodStr);
   1052                 return false;
   1053             }
   1054         }
   1055     }
   1056 
   1057     /**
   1058      * Initiate TDLS setup with the specified AP.
   1059      *
   1060      * @param macAddress MAC Address of the AP.
   1061      * @return true if request is sent successfully, false otherwise.
   1062      */
   1063     public boolean initiateTdlsSetup(String macAddress) {
   1064         try {
   1065             return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
   1066         } catch (IllegalArgumentException e) {
   1067             Log.e(TAG, "Illegal argument " + macAddress, e);
   1068             return false;
   1069         }
   1070     }
   1071     /** See ISupplicantStaIface.hal for documentation */
   1072     private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
   1073         synchronized (mLock) {
   1074             final String methodStr = "initiateTdlsSetup";
   1075             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1076             try {
   1077                 SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
   1078                 return checkStatusAndLogFailure(status, methodStr);
   1079             } catch (RemoteException e) {
   1080                 handleRemoteException(e, methodStr);
   1081                 return false;
   1082             }
   1083         }
   1084     }
   1085 
   1086     /**
   1087      * Initiate TDLS teardown with the specified AP.
   1088      * @param macAddress MAC Address of the AP.
   1089      * @return true if request is sent successfully, false otherwise.
   1090      */
   1091     public boolean initiateTdlsTeardown(String macAddress) {
   1092         try {
   1093             return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
   1094         } catch (IllegalArgumentException e) {
   1095             Log.e(TAG, "Illegal argument " + macAddress, e);
   1096             return false;
   1097         }
   1098     }
   1099 
   1100     /** See ISupplicantStaIface.hal for documentation */
   1101     private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
   1102         synchronized (mLock) {
   1103             final String methodStr = "initiateTdlsTeardown";
   1104             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1105             try {
   1106                 SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
   1107                 return checkStatusAndLogFailure(status, methodStr);
   1108             } catch (RemoteException e) {
   1109                 handleRemoteException(e, methodStr);
   1110                 return false;
   1111             }
   1112         }
   1113     }
   1114 
   1115     /**
   1116      * Request the specified ANQP elements |elements| from the specified AP |bssid|.
   1117      *
   1118      * @param bssid BSSID of the AP
   1119      * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
   1120      * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
   1121      * @return true if request is sent successfully, false otherwise.
   1122      */
   1123     public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
   1124                                      ArrayList<Integer> hs20SubTypes) {
   1125         try {
   1126             return initiateAnqpQuery(
   1127                     NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
   1128         } catch (IllegalArgumentException e) {
   1129             Log.e(TAG, "Illegal argument " + bssid, e);
   1130             return false;
   1131         }
   1132     }
   1133 
   1134     /** See ISupplicantStaIface.hal for documentation */
   1135     private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
   1136             java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
   1137         synchronized (mLock) {
   1138             final String methodStr = "initiateAnqpQuery";
   1139             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1140             try {
   1141                 SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
   1142                         infoElements, subTypes);
   1143                 return checkStatusAndLogFailure(status, methodStr);
   1144             } catch (RemoteException e) {
   1145                 handleRemoteException(e, methodStr);
   1146                 return false;
   1147             }
   1148         }
   1149     }
   1150 
   1151     /**
   1152      * Request the specified ANQP ICON from the specified AP |bssid|.
   1153      *
   1154      * @param bssid BSSID of the AP
   1155      * @param fileName Name of the file to request.
   1156      * @return true if request is sent successfully, false otherwise.
   1157      */
   1158     public boolean initiateHs20IconQuery(String bssid, String fileName) {
   1159         try {
   1160             return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
   1161         } catch (IllegalArgumentException e) {
   1162             Log.e(TAG, "Illegal argument " + bssid, e);
   1163             return false;
   1164         }
   1165     }
   1166 
   1167     /** See ISupplicantStaIface.hal for documentation */
   1168     private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
   1169         synchronized (mLock) {
   1170             final String methodStr = "initiateHs20IconQuery";
   1171             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1172             try {
   1173                 SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
   1174                         fileName);
   1175                 return checkStatusAndLogFailure(status, methodStr);
   1176             } catch (RemoteException e) {
   1177                 handleRemoteException(e, methodStr);
   1178                 return false;
   1179             }
   1180         }
   1181     }
   1182 
   1183     /**
   1184      * Makes a callback to HIDL to getMacAddress from supplicant
   1185      *
   1186      * @return string containing the MAC address, or null on a failed call
   1187      */
   1188     public String getMacAddress() {
   1189         synchronized (mLock) {
   1190             final String methodStr = "getMacAddress";
   1191             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
   1192             Mutable<String> gotMac = new Mutable<>();
   1193             try {
   1194                 mISupplicantStaIface.getMacAddress((SupplicantStatus status,
   1195                         byte[/* 6 */] macAddr) -> {
   1196                     if (checkStatusAndLogFailure(status, methodStr)) {
   1197                         gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
   1198                     }
   1199                 });
   1200             } catch (RemoteException e) {
   1201                 handleRemoteException(e, methodStr);
   1202             }
   1203             return gotMac.value;
   1204         }
   1205     }
   1206 
   1207     /**
   1208      * Start using the added RX filters.
   1209      *
   1210      * @return true if request is sent successfully, false otherwise.
   1211      */
   1212     public boolean startRxFilter() {
   1213         synchronized (mLock) {
   1214             final String methodStr = "startRxFilter";
   1215             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1216             try {
   1217                 SupplicantStatus status = mISupplicantStaIface.startRxFilter();
   1218                 return checkStatusAndLogFailure(status, methodStr);
   1219             } catch (RemoteException e) {
   1220                 handleRemoteException(e, methodStr);
   1221                 return false;
   1222             }
   1223         }
   1224     }
   1225 
   1226     /**
   1227      * Stop using the added RX filters.
   1228      *
   1229      * @return true if request is sent successfully, false otherwise.
   1230      */
   1231     public boolean stopRxFilter() {
   1232         synchronized (mLock) {
   1233             final String methodStr = "stopRxFilter";
   1234             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1235             try {
   1236                 SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
   1237                 return checkStatusAndLogFailure(status, methodStr);
   1238             } catch (RemoteException e) {
   1239                 handleRemoteException(e, methodStr);
   1240                 return false;
   1241             }
   1242         }
   1243     }
   1244 
   1245     /**
   1246      * Add an RX filter.
   1247      *
   1248      * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
   1249      *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
   1250      * @return true if request is sent successfully, false otherwise.
   1251      */
   1252     public boolean addRxFilter(int type) {
   1253         byte halType;
   1254         switch (type) {
   1255             case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
   1256                 halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
   1257                 break;
   1258             case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
   1259                 halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
   1260                 break;
   1261             default:
   1262                 Log.e(TAG, "Invalid Rx Filter type: " + type);
   1263                 return false;
   1264         }
   1265         return addRxFilter(halType);
   1266     }
   1267 
   1268     public boolean addRxFilter(byte type) {
   1269         synchronized (mLock) {
   1270             final String methodStr = "addRxFilter";
   1271             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1272             try {
   1273                 SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
   1274                 return checkStatusAndLogFailure(status, methodStr);
   1275             } catch (RemoteException e) {
   1276                 handleRemoteException(e, methodStr);
   1277                 return false;
   1278             }
   1279         }
   1280     }
   1281 
   1282     /**
   1283      * Remove an RX filter.
   1284      *
   1285      * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
   1286      *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
   1287      * @return true if request is sent successfully, false otherwise.
   1288      */
   1289     public boolean removeRxFilter(int type) {
   1290         byte halType;
   1291         switch (type) {
   1292             case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
   1293                 halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
   1294                 break;
   1295             case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
   1296                 halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
   1297                 break;
   1298             default:
   1299                 Log.e(TAG, "Invalid Rx Filter type: " + type);
   1300                 return false;
   1301         }
   1302         return removeRxFilter(halType);
   1303     }
   1304 
   1305     public boolean removeRxFilter(byte type) {
   1306         synchronized (mLock) {
   1307             final String methodStr = "removeRxFilter";
   1308             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1309             try {
   1310                 SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
   1311                 return checkStatusAndLogFailure(status, methodStr);
   1312             } catch (RemoteException e) {
   1313                 handleRemoteException(e, methodStr);
   1314                 return false;
   1315             }
   1316         }
   1317     }
   1318 
   1319     /**
   1320      * Set Bt co existense mode.
   1321      *
   1322      * @param mode one of the above {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_DISABLED},
   1323      *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_ENABLED} or
   1324      *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_SENSE}.
   1325      * @return true if request is sent successfully, false otherwise.
   1326      */
   1327     public boolean setBtCoexistenceMode(int mode) {
   1328         byte halMode;
   1329         switch (mode) {
   1330             case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
   1331                 halMode = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
   1332                 break;
   1333             case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
   1334                 halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
   1335                 break;
   1336             case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
   1337                 halMode = ISupplicantStaIface.BtCoexistenceMode.SENSE;
   1338                 break;
   1339             default:
   1340                 Log.e(TAG, "Invalid Bt Coex mode: " + mode);
   1341                 return false;
   1342         }
   1343         return setBtCoexistenceMode(halMode);
   1344     }
   1345 
   1346     private boolean setBtCoexistenceMode(byte mode) {
   1347         synchronized (mLock) {
   1348             final String methodStr = "setBtCoexistenceMode";
   1349             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1350             try {
   1351                 SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
   1352                 return checkStatusAndLogFailure(status, methodStr);
   1353             } catch (RemoteException e) {
   1354                 handleRemoteException(e, methodStr);
   1355                 return false;
   1356             }
   1357         }
   1358     }
   1359 
   1360     /** Enable or disable BT coexistence mode.
   1361      *
   1362      * @param enable true to enable, false to disable.
   1363      * @return true if request is sent successfully, false otherwise.
   1364      */
   1365     public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
   1366         synchronized (mLock) {
   1367             final String methodStr = "setBtCoexistenceScanModeEnabled";
   1368             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1369             try {
   1370                 SupplicantStatus status =
   1371                         mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
   1372                 return checkStatusAndLogFailure(status, methodStr);
   1373             } catch (RemoteException e) {
   1374                 handleRemoteException(e, methodStr);
   1375                 return false;
   1376             }
   1377         }
   1378     }
   1379 
   1380     /**
   1381      * Enable or disable suspend mode optimizations.
   1382      *
   1383      * @param enable true to enable, false otherwise.
   1384      * @return true if request is sent successfully, false otherwise.
   1385      */
   1386     public boolean setSuspendModeEnabled(boolean enable) {
   1387         synchronized (mLock) {
   1388             final String methodStr = "setSuspendModeEnabled";
   1389             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1390             try {
   1391                 SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
   1392                 return checkStatusAndLogFailure(status, methodStr);
   1393             } catch (RemoteException e) {
   1394                 handleRemoteException(e, methodStr);
   1395                 return false;
   1396             }
   1397         }
   1398     }
   1399 
   1400     /**
   1401      * Set country code.
   1402      *
   1403      * @param codeStr 2 byte ASCII string. For ex: US, CA.
   1404      * @return true if request is sent successfully, false otherwise.
   1405      */
   1406     public boolean setCountryCode(String codeStr) {
   1407         if (TextUtils.isEmpty(codeStr)) return false;
   1408         return setCountryCode(NativeUtil.stringToByteArray(codeStr));
   1409     }
   1410 
   1411     /** See ISupplicantStaIface.hal for documentation */
   1412     private boolean setCountryCode(byte[/* 2 */] code) {
   1413         synchronized (mLock) {
   1414             final String methodStr = "setCountryCode";
   1415             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1416             try {
   1417                 SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
   1418                 return checkStatusAndLogFailure(status, methodStr);
   1419             } catch (RemoteException e) {
   1420                 handleRemoteException(e, methodStr);
   1421                 return false;
   1422             }
   1423         }
   1424     }
   1425 
   1426     /**
   1427      * Start WPS pin registrar operation with the specified peer and pin.
   1428      *
   1429      * @param bssidStr BSSID of the peer.
   1430      * @param pin Pin to be used.
   1431      * @return true if request is sent successfully, false otherwise.
   1432      */
   1433     public boolean startWpsRegistrar(String bssidStr, String pin) {
   1434         if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
   1435         try {
   1436             return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
   1437         } catch (IllegalArgumentException e) {
   1438             Log.e(TAG, "Illegal argument " + bssidStr, e);
   1439             return false;
   1440         }
   1441     }
   1442 
   1443     /** See ISupplicantStaIface.hal for documentation */
   1444     private boolean startWpsRegistrar(byte[/* 6 */] bssid, String pin) {
   1445         synchronized (mLock) {
   1446             final String methodStr = "startWpsRegistrar";
   1447             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1448             try {
   1449                 SupplicantStatus status = mISupplicantStaIface.startWpsRegistrar(bssid, pin);
   1450                 return checkStatusAndLogFailure(status, methodStr);
   1451             } catch (RemoteException e) {
   1452                 handleRemoteException(e, methodStr);
   1453                 return false;
   1454             }
   1455         }
   1456     }
   1457 
   1458     /**
   1459      * Start WPS pin display operation with the specified peer.
   1460      *
   1461      * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
   1462      * @return true if request is sent successfully, false otherwise.
   1463      */
   1464     public boolean startWpsPbc(String bssidStr) {
   1465         try {
   1466             return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
   1467         } catch (IllegalArgumentException e) {
   1468             Log.e(TAG, "Illegal argument " + bssidStr, e);
   1469             return false;
   1470         }
   1471     }
   1472 
   1473     /** See ISupplicantStaIface.hal for documentation */
   1474     private boolean startWpsPbc(byte[/* 6 */] bssid) {
   1475         synchronized (mLock) {
   1476             final String methodStr = "startWpsPbc";
   1477             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1478             try {
   1479                 SupplicantStatus status = mISupplicantStaIface.startWpsPbc(bssid);
   1480                 return checkStatusAndLogFailure(status, methodStr);
   1481             } catch (RemoteException e) {
   1482                 handleRemoteException(e, methodStr);
   1483                 return false;
   1484             }
   1485         }
   1486     }
   1487 
   1488     /**
   1489      * Start WPS pin keypad operation with the specified pin.
   1490      *
   1491      * @param pin Pin to be used.
   1492      * @return true if request is sent successfully, false otherwise.
   1493      */
   1494     public boolean startWpsPinKeypad(String pin) {
   1495         if (TextUtils.isEmpty(pin)) return false;
   1496         synchronized (mLock) {
   1497             final String methodStr = "startWpsPinKeypad";
   1498             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1499             try {
   1500                 SupplicantStatus status = mISupplicantStaIface.startWpsPinKeypad(pin);
   1501                 return checkStatusAndLogFailure(status, methodStr);
   1502             } catch (RemoteException e) {
   1503                 handleRemoteException(e, methodStr);
   1504                 return false;
   1505             }
   1506         }
   1507     }
   1508 
   1509     /**
   1510      * Start WPS pin display operation with the specified peer.
   1511      *
   1512      * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
   1513      * @return new pin generated on success, null otherwise.
   1514      */
   1515     public String startWpsPinDisplay(String bssidStr) {
   1516         try {
   1517             return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
   1518         } catch (IllegalArgumentException e) {
   1519             Log.e(TAG, "Illegal argument " + bssidStr, e);
   1520             return null;
   1521         }
   1522     }
   1523 
   1524     /** See ISupplicantStaIface.hal for documentation */
   1525     private String startWpsPinDisplay(byte[/* 6 */] bssid) {
   1526         synchronized (mLock) {
   1527             final String methodStr = "startWpsPinDisplay";
   1528             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
   1529             final Mutable<String> gotPin = new Mutable<>();
   1530             try {
   1531                 mISupplicantStaIface.startWpsPinDisplay(bssid,
   1532                         (SupplicantStatus status, String pin) -> {
   1533                             if (checkStatusAndLogFailure(status, methodStr)) {
   1534                                 gotPin.value = pin;
   1535                             }
   1536                         });
   1537             } catch (RemoteException e) {
   1538                 handleRemoteException(e, methodStr);
   1539             }
   1540             return gotPin.value;
   1541         }
   1542     }
   1543 
   1544     /**
   1545      * Cancels any ongoing WPS requests.
   1546      *
   1547      * @return true if request is sent successfully, false otherwise.
   1548      */
   1549     public boolean cancelWps() {
   1550         synchronized (mLock) {
   1551             final String methodStr = "cancelWps";
   1552             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1553             try {
   1554                 SupplicantStatus status = mISupplicantStaIface.cancelWps();
   1555                 return checkStatusAndLogFailure(status, methodStr);
   1556             } catch (RemoteException e) {
   1557                 handleRemoteException(e, methodStr);
   1558                 return false;
   1559             }
   1560         }
   1561     }
   1562 
   1563     /**
   1564      * Sets whether to use external sim for SIM/USIM processing.
   1565      *
   1566      * @param useExternalSim true to enable, false otherwise.
   1567      * @return true if request is sent successfully, false otherwise.
   1568      */
   1569     public boolean setExternalSim(boolean useExternalSim) {
   1570         synchronized (mLock) {
   1571             final String methodStr = "setExternalSim";
   1572             if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
   1573             try {
   1574                 SupplicantStatus status = mISupplicantStaIface.setExternalSim(useExternalSim);
   1575                 return checkStatusAndLogFailure(status, methodStr);
   1576             } catch (RemoteException e) {
   1577                 handleRemoteException(e, methodStr);
   1578                 return false;
   1579             }
   1580         }
   1581     }
   1582 
   1583     /** See ISupplicant.hal for documentation */
   1584     public boolean enableAutoReconnect(boolean enable) {
   1585         synchronized (mLock) {
   1586             final String methodStr = "enableAutoReconnect";
   1587             if (!checkSupplicantAndLogFailure(methodStr)) return false;
   1588             try {
   1589                 SupplicantStatus status = mISupplicantStaIface.enableAutoReconnect(enable);
   1590                 return checkStatusAndLogFailure(status, methodStr);
   1591             } catch (RemoteException e) {
   1592                 handleRemoteException(e, methodStr);
   1593                 return false;
   1594             }
   1595         }
   1596     }
   1597 
   1598     /**
   1599      * Set the debug log level for wpa_supplicant
   1600      *
   1601      * @param turnOnVerbose Whether to turn on verbose logging or not.
   1602      * @return true if request is sent successfully, false otherwise.
   1603      */
   1604     public boolean setLogLevel(boolean turnOnVerbose) {
   1605         int logLevel = turnOnVerbose
   1606                 ? ISupplicant.DebugLevel.DEBUG
   1607                 : ISupplicant.DebugLevel.INFO;
   1608         return setDebugParams(logLevel, false, false);
   1609     }
   1610 
   1611     /** See ISupplicant.hal for documentation */
   1612     private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
   1613         synchronized (mLock) {
   1614             final String methodStr = "setDebugParams";
   1615             if (!checkSupplicantAndLogFailure(methodStr)) return false;
   1616             try {
   1617                 SupplicantStatus status =
   1618                         mISupplicant.setDebugParams(level, showTimestamp, showKeys);
   1619                 return checkStatusAndLogFailure(status, methodStr);
   1620             } catch (RemoteException e) {
   1621                 handleRemoteException(e, methodStr);
   1622                 return false;
   1623             }
   1624         }
   1625     }
   1626 
   1627     /**
   1628      * Set concurrency priority between P2P & STA operations.
   1629      *
   1630      * @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
   1631      *                            false otherwise.
   1632      * @return true if request is sent successfully, false otherwise.
   1633      */
   1634     public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
   1635         if (isStaHigherPriority) {
   1636             return setConcurrencyPriority(IfaceType.STA);
   1637         } else {
   1638             return setConcurrencyPriority(IfaceType.P2P);
   1639         }
   1640     }
   1641 
   1642     /** See ISupplicant.hal for documentation */
   1643     private boolean setConcurrencyPriority(int type) {
   1644         synchronized (mLock) {
   1645             final String methodStr = "setConcurrencyPriority";
   1646             if (!checkSupplicantAndLogFailure(methodStr)) return false;
   1647             try {
   1648                 SupplicantStatus status = mISupplicant.setConcurrencyPriority(type);
   1649                 return checkStatusAndLogFailure(status, methodStr);
   1650             } catch (RemoteException e) {
   1651                 handleRemoteException(e, methodStr);
   1652                 return false;
   1653             }
   1654         }
   1655     }
   1656 
   1657     /**
   1658      * Returns false if Supplicant is null, and logs failure to call methodStr
   1659      */
   1660     private boolean checkSupplicantAndLogFailure(final String methodStr) {
   1661         if (mISupplicant == null) {
   1662             Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
   1663             return false;
   1664         }
   1665         return true;
   1666     }
   1667 
   1668     /**
   1669      * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
   1670      */
   1671     private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
   1672         if (mISupplicantStaIface == null) {
   1673             Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
   1674             return false;
   1675         }
   1676         return true;
   1677     }
   1678 
   1679     /**
   1680      * Returns true if provided status code is SUCCESS, logs debug message and returns false
   1681      * otherwise
   1682      */
   1683     private boolean checkStatusAndLogFailure(SupplicantStatus status,
   1684             final String methodStr) {
   1685         if (status.code != SupplicantStatusCode.SUCCESS) {
   1686             Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: "
   1687                     + supplicantStatusCodeToString(status.code) + ", " + status.debugMessage);
   1688             return false;
   1689         } else {
   1690             if (mVerboseLoggingEnabled) {
   1691                 Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
   1692             }
   1693             return true;
   1694         }
   1695     }
   1696 
   1697     /**
   1698      * Helper function to log callbacks.
   1699      */
   1700     private void logCallback(final String methodStr) {
   1701         if (mVerboseLoggingEnabled) {
   1702             Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
   1703         }
   1704     }
   1705 
   1706 
   1707     private void handleRemoteException(RemoteException e, String methodStr) {
   1708         supplicantServiceDiedHandler();
   1709         Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
   1710     }
   1711 
   1712     /**
   1713      * Converts SupplicantStatus code values to strings for debug logging
   1714      * TODO(b/34811152) Remove this, or make it more break resistance
   1715      */
   1716     public static String supplicantStatusCodeToString(int code) {
   1717         switch (code) {
   1718             case 0:
   1719                 return "SUCCESS";
   1720             case 1:
   1721                 return "FAILURE_UNKNOWN";
   1722             case 2:
   1723                 return "FAILURE_ARGS_INVALID";
   1724             case 3:
   1725                 return "FAILURE_IFACE_INVALID";
   1726             case 4:
   1727                 return "FAILURE_IFACE_UNKNOWN";
   1728             case 5:
   1729                 return "FAILURE_IFACE_EXISTS";
   1730             case 6:
   1731                 return "FAILURE_IFACE_DISABLED";
   1732             case 7:
   1733                 return "FAILURE_IFACE_NOT_DISCONNECTED";
   1734             case 8:
   1735                 return "FAILURE_NETWORK_INVALID";
   1736             case 9:
   1737                 return "FAILURE_NETWORK_UNKNOWN";
   1738             default:
   1739                 return "??? UNKNOWN_CODE";
   1740         }
   1741     }
   1742 
   1743 
   1744     /**
   1745      * Converts the Wps config method string to the equivalent enum value.
   1746      */
   1747     private static short stringToWpsConfigMethod(String configMethod) {
   1748         switch (configMethod) {
   1749             case "usba":
   1750                 return WpsConfigMethods.USBA;
   1751             case "ethernet":
   1752                 return WpsConfigMethods.ETHERNET;
   1753             case "label":
   1754                 return WpsConfigMethods.LABEL;
   1755             case "display":
   1756                 return WpsConfigMethods.DISPLAY;
   1757             case "int_nfc_token":
   1758                 return WpsConfigMethods.INT_NFC_TOKEN;
   1759             case "ext_nfc_token":
   1760                 return WpsConfigMethods.EXT_NFC_TOKEN;
   1761             case "nfc_interface":
   1762                 return WpsConfigMethods.NFC_INTERFACE;
   1763             case "push_button":
   1764                 return WpsConfigMethods.PUSHBUTTON;
   1765             case "keypad":
   1766                 return WpsConfigMethods.KEYPAD;
   1767             case "virtual_push_button":
   1768                 return WpsConfigMethods.VIRT_PUSHBUTTON;
   1769             case "physical_push_button":
   1770                 return WpsConfigMethods.PHY_PUSHBUTTON;
   1771             case "p2ps":
   1772                 return WpsConfigMethods.P2PS;
   1773             case "virtual_display":
   1774                 return WpsConfigMethods.VIRT_DISPLAY;
   1775             case "physical_display":
   1776                 return WpsConfigMethods.PHY_DISPLAY;
   1777             default:
   1778                 throw new IllegalArgumentException(
   1779                         "Invalid WPS config method: " + configMethod);
   1780         }
   1781     }
   1782 
   1783     /**
   1784      * Converts the supplicant state received from HIDL to the equivalent framework state.
   1785      */
   1786     private static SupplicantState supplicantHidlStateToFrameworkState(int state) {
   1787         switch (state) {
   1788             case ISupplicantStaIfaceCallback.State.DISCONNECTED:
   1789                 return SupplicantState.DISCONNECTED;
   1790             case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
   1791                 return SupplicantState.INTERFACE_DISABLED;
   1792             case ISupplicantStaIfaceCallback.State.INACTIVE:
   1793                 return SupplicantState.INACTIVE;
   1794             case ISupplicantStaIfaceCallback.State.SCANNING:
   1795                 return SupplicantState.SCANNING;
   1796             case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
   1797                 return SupplicantState.AUTHENTICATING;
   1798             case ISupplicantStaIfaceCallback.State.ASSOCIATING:
   1799                 return SupplicantState.ASSOCIATING;
   1800             case ISupplicantStaIfaceCallback.State.ASSOCIATED:
   1801                 return SupplicantState.ASSOCIATED;
   1802             case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
   1803                 return SupplicantState.FOUR_WAY_HANDSHAKE;
   1804             case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
   1805                 return SupplicantState.GROUP_HANDSHAKE;
   1806             case ISupplicantStaIfaceCallback.State.COMPLETED:
   1807                 return SupplicantState.COMPLETED;
   1808             default:
   1809                 throw new IllegalArgumentException("Invalid state: " + state);
   1810         }
   1811     }
   1812 
   1813     private static class Mutable<E> {
   1814         public E value;
   1815 
   1816         Mutable() {
   1817             value = null;
   1818         }
   1819 
   1820         Mutable(E value) {
   1821             this.value = value;
   1822         }
   1823     }
   1824 
   1825     private class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub {
   1826         private static final int WLAN_REASON_IE_IN_4WAY_DIFFERS = 17; // IEEE 802.11i
   1827         private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch
   1828 
   1829         /**
   1830          * Parses the provided payload into an ANQP element.
   1831          *
   1832          * @param infoID  Element type.
   1833          * @param payload Raw payload bytes.
   1834          * @return AnqpElement instance on success, null on failure.
   1835          */
   1836         private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
   1837                                              ArrayList<Byte> payload) {
   1838             try {
   1839                 return Constants.getANQPElementID(infoID) != null
   1840                         ? ANQPParser.parseElement(
   1841                         infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
   1842                         : ANQPParser.parseHS20Element(
   1843                         infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
   1844             } catch (IOException | BufferUnderflowException e) {
   1845                 Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
   1846                 return null;
   1847             }
   1848         }
   1849 
   1850         /**
   1851          * Parse the ANQP element data and add to the provided elements map if successful.
   1852          *
   1853          * @param elementsMap Map to add the parsed out element to.
   1854          * @param infoID  Element type.
   1855          * @param payload Raw payload bytes.
   1856          */
   1857         private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
   1858                                          Constants.ANQPElementType infoID,
   1859                                          ArrayList<Byte> payload) {
   1860             if (payload == null || payload.isEmpty()) return;
   1861             ANQPElement element = parseAnqpElement(infoID, payload);
   1862             if (element != null) {
   1863                 elementsMap.put(infoID, element);
   1864             }
   1865         }
   1866 
   1867         @Override
   1868         public void onNetworkAdded(int id) {
   1869             logCallback("onNetworkAdded");
   1870         }
   1871 
   1872         @Override
   1873         public void onNetworkRemoved(int id) {
   1874             logCallback("onNetworkRemoved");
   1875         }
   1876 
   1877         @Override
   1878         public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
   1879                                    ArrayList<Byte> ssid) {
   1880             logCallback("onStateChanged");
   1881             synchronized (mLock) {
   1882                 SupplicantState newSupplicantState = supplicantHidlStateToFrameworkState(newState);
   1883                 WifiSsid wifiSsid =
   1884                         WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
   1885                 String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
   1886                 mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
   1887                 if (newSupplicantState == SupplicantState.COMPLETED) {
   1888                     mWifiMonitor.broadcastNetworkConnectionEvent(
   1889                             mIfaceName, getCurrentNetworkId(), bssidStr);
   1890                 }
   1891                 mWifiMonitor.broadcastSupplicantStateChangeEvent(
   1892                         mIfaceName, getCurrentNetworkId(), wifiSsid, bssidStr, newSupplicantState);
   1893             }
   1894         }
   1895 
   1896         @Override
   1897         public void onAnqpQueryDone(byte[/* 6 */] bssid,
   1898                                     ISupplicantStaIfaceCallback.AnqpData data,
   1899                                     ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
   1900             logCallback("onAnqpQueryDone");
   1901             synchronized (mLock) {
   1902                 Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
   1903                 addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
   1904                 addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
   1905                 addAnqpElementToMap(
   1906                         elementsMap, ANQPIPAddrAvailability, data.ipAddrTypeAvailability);
   1907                 addAnqpElementToMap(elementsMap, ANQPNAIRealm, data.naiRealm);
   1908                 addAnqpElementToMap(elementsMap, ANQP3GPPNetwork, data.anqp3gppCellularNetwork);
   1909                 addAnqpElementToMap(elementsMap, ANQPDomName, data.domainName);
   1910                 addAnqpElementToMap(elementsMap, HSFriendlyName, hs20Data.operatorFriendlyName);
   1911                 addAnqpElementToMap(elementsMap, HSWANMetrics, hs20Data.wanMetrics);
   1912                 addAnqpElementToMap(elementsMap, HSConnCapability, hs20Data.connectionCapability);
   1913                 addAnqpElementToMap(elementsMap, HSOSUProviders, hs20Data.osuProvidersList);
   1914                 mWifiMonitor.broadcastAnqpDoneEvent(
   1915                         mIfaceName, new AnqpEvent(NativeUtil.macAddressToLong(bssid), elementsMap));
   1916             }
   1917         }
   1918 
   1919         @Override
   1920         public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
   1921                                         ArrayList<Byte> data) {
   1922             logCallback("onHs20IconQueryDone");
   1923             synchronized (mLock) {
   1924                 mWifiMonitor.broadcastIconDoneEvent(
   1925                         mIfaceName,
   1926                         new IconEvent(NativeUtil.macAddressToLong(bssid), fileName, data.size(),
   1927                                 NativeUtil.byteArrayFromArrayList(data)));
   1928             }
   1929         }
   1930 
   1931         @Override
   1932         public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
   1933             logCallback("onHs20SubscriptionRemediation");
   1934             synchronized (mLock) {
   1935                 mWifiMonitor.broadcastWnmEvent(
   1936                         mIfaceName,
   1937                         new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod));
   1938             }
   1939         }
   1940 
   1941         @Override
   1942         public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
   1943                                                int reAuthDelayInSec, String url) {
   1944             logCallback("onHs20DeauthImminentNotice");
   1945             synchronized (mLock) {
   1946                 mWifiMonitor.broadcastWnmEvent(
   1947                         mIfaceName,
   1948                         new WnmData(NativeUtil.macAddressToLong(bssid), url,
   1949                                 reasonCode == WnmData.ESS, reAuthDelayInSec));
   1950             }
   1951         }
   1952 
   1953         @Override
   1954         public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
   1955             logCallback("onDisconnected");
   1956             synchronized (mLock) {
   1957                 if (mVerboseLoggingEnabled) {
   1958                     Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway
   1959                             + " locallyGenerated=" + locallyGenerated
   1960                             + " reasonCode=" + reasonCode);
   1961                 }
   1962                 if (mStateIsFourway
   1963                         && (!locallyGenerated || reasonCode != WLAN_REASON_IE_IN_4WAY_DIFFERS)) {
   1964                     mWifiMonitor.broadcastAuthenticationFailureEvent(
   1965                             mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
   1966                 }
   1967                 mWifiMonitor.broadcastNetworkDisconnectionEvent(
   1968                         mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
   1969                         NativeUtil.macAddressFromByteArray(bssid));
   1970             }
   1971         }
   1972 
   1973         @Override
   1974         public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) {
   1975             logCallback("onAssociationRejected");
   1976             synchronized (mLock) {
   1977                 mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode, timedOut,
   1978                         NativeUtil.macAddressFromByteArray(bssid));
   1979             }
   1980         }
   1981 
   1982         @Override
   1983         public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
   1984             logCallback("onAuthenticationTimeout");
   1985             synchronized (mLock) {
   1986                 mWifiMonitor.broadcastAuthenticationFailureEvent(
   1987                         mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT);
   1988             }
   1989         }
   1990 
   1991         @Override
   1992         public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
   1993             logCallback("onBssidChanged");
   1994             synchronized (mLock) {
   1995                 if (reason == BssidChangeReason.ASSOC_START) {
   1996                     mWifiMonitor.broadcastTargetBssidEvent(
   1997                             mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
   1998                 } else if (reason == BssidChangeReason.ASSOC_COMPLETE) {
   1999                     mWifiMonitor.broadcastAssociatedBssidEvent(
   2000                             mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
   2001                 }
   2002             }
   2003         }
   2004 
   2005         @Override
   2006         public void onEapFailure() {
   2007             logCallback("onEapFailure");
   2008             synchronized (mLock) {
   2009                 mWifiMonitor.broadcastAuthenticationFailureEvent(
   2010                         mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE);
   2011             }
   2012         }
   2013 
   2014         @Override
   2015         public void onWpsEventSuccess() {
   2016             logCallback("onWpsEventSuccess");
   2017             synchronized (mLock) {
   2018                 mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName);
   2019             }
   2020         }
   2021 
   2022         @Override
   2023         public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
   2024             logCallback("onWpsEventFail");
   2025             synchronized (mLock) {
   2026                 if (configError == WpsConfigError.MSG_TIMEOUT
   2027                         && errorInd == WpsErrorIndication.NO_ERROR) {
   2028                     mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
   2029                 } else {
   2030                     mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd);
   2031                 }
   2032             }
   2033         }
   2034 
   2035         @Override
   2036         public void onWpsEventPbcOverlap() {
   2037             logCallback("onWpsEventPbcOverlap");
   2038             synchronized (mLock) {
   2039                 mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
   2040             }
   2041         }
   2042 
   2043         @Override
   2044         public void onExtRadioWorkStart(int id) {
   2045             logCallback("onExtRadioWorkStart");
   2046         }
   2047 
   2048         @Override
   2049         public void onExtRadioWorkTimeout(int id) {
   2050             logCallback("onExtRadioWorkTimeout");
   2051         }
   2052     }
   2053 
   2054     private void logd(String s) {
   2055         Log.d(TAG, s);
   2056     }
   2057 
   2058     private void logi(String s) {
   2059         Log.i(TAG, s);
   2060     }
   2061 
   2062     private void loge(String s) {
   2063         Log.e(TAG, s);
   2064     }
   2065 }
   2066