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