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