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 */
16package com.android.server.wifi;
17
18import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQP3GPPNetwork;
19import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPDomName;
20import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPIPAddrAvailability;
21import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPNAIRealm;
22import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPRoamingConsortium;
23import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPVenueName;
24import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSConnCapability;
25import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSFriendlyName;
26import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSOSUProviders;
27import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics;
28
29import android.annotation.NonNull;
30import android.content.Context;
31import android.hardware.wifi.supplicant.V1_0.ISupplicant;
32import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
33import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
34import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
35import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
36import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
37import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
38import android.hardware.wifi.supplicant.V1_0.IfaceType;
39import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
40import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
41import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
42import android.hidl.manager.V1_0.IServiceManager;
43import android.hidl.manager.V1_0.IServiceNotification;
44import android.net.IpConfiguration;
45import android.net.wifi.SupplicantState;
46import android.net.wifi.WifiConfiguration;
47import android.net.wifi.WifiManager;
48import android.net.wifi.WifiSsid;
49import android.os.HwRemoteBinder;
50import android.os.RemoteException;
51import android.text.TextUtils;
52import android.util.Log;
53import android.util.Pair;
54import android.util.SparseArray;
55
56import com.android.server.wifi.hotspot2.AnqpEvent;
57import com.android.server.wifi.hotspot2.IconEvent;
58import com.android.server.wifi.hotspot2.WnmData;
59import com.android.server.wifi.hotspot2.anqp.ANQPElement;
60import com.android.server.wifi.hotspot2.anqp.ANQPParser;
61import com.android.server.wifi.hotspot2.anqp.Constants;
62import com.android.server.wifi.util.NativeUtil;
63
64import java.io.IOException;
65import java.nio.BufferUnderflowException;
66import java.nio.ByteBuffer;
67import java.nio.ByteOrder;
68import java.util.ArrayList;
69import java.util.HashMap;
70import java.util.List;
71import java.util.Map;
72import java.util.regex.Matcher;
73import 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 */
79public 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