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