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