SupplicantStaIfaceHal.java revision 1c353f3fca322aab2fff5369a55876a91a112775
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    private static final String SERVICE_MANAGER_NAME = "manager";
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(SERVICE_MANAGER_NAME);
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            if (!network.loadWifiConfiguration(config, networkExtra)) {
469                Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId);
470                return false;
471            }
472            // Set the default IP assignments.
473            config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
474            config.setProxySettings(IpConfiguration.ProxySettings.NONE);
475
476            networkExtras.put(networkId, networkExtra);
477            String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
478            final WifiConfiguration duplicateConfig = configs.put(configKey, config);
479            if (duplicateConfig != null) {
480                // The network is already known. Overwrite the duplicate entry.
481                Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
482                removeNetwork(duplicateConfig.networkId);
483                networkExtras.remove(duplicateConfig.networkId);
484            }
485        }
486        return true;
487    }
488
489    /**
490     * Remove all networks from supplicant
491     */
492    public boolean removeAllNetworks() {
493        synchronized (mLock) {
494            ArrayList<Integer> networks = listNetworks();
495            if (networks == null) {
496                Log.e(TAG, "removeAllNetworks failed, got null networks");
497                return false;
498            }
499            for (int id : networks) {
500                if (!removeNetwork(id)) {
501                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
502                    return false;
503                }
504            }
505        }
506        return true;
507    }
508
509    /**
510     * Set the currently configured network's bssid.
511     *
512     * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
513     * @return true if succeeds, false otherwise.
514     */
515    public boolean setCurrentNetworkBssid(String bssidStr) {
516        if (mCurrentNetwork == null) return false;
517        return mCurrentNetwork.setBssid(bssidStr);
518    }
519
520    /**
521     * Get the currently configured network's WPS NFC token.
522     *
523     * @return Hex string corresponding to the WPS NFC token.
524     */
525    public String getCurrentNetworkWpsNfcConfigurationToken() {
526        if (mCurrentNetwork == null) return null;
527        return mCurrentNetwork.getWpsNfcConfigurationToken();
528    }
529
530    /**
531     * Send the eap identity response for the currently configured network.
532     *
533     * @param identityStr String to send.
534     * @return true if succeeds, false otherwise.
535     */
536    public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) {
537        if (mCurrentNetwork == null) return false;
538        return mCurrentNetwork.sendNetworkEapIdentityResponse(identityStr);
539    }
540
541    /**
542     * Send the eap sim gsm auth response for the currently configured network.
543     *
544     * @param paramsStr String to send.
545     * @return true if succeeds, false otherwise.
546     */
547    public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) {
548        if (mCurrentNetwork == null) return false;
549        return mCurrentNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr);
550    }
551
552    /**
553     * Send the eap sim gsm auth failure for the currently configured network.
554     *
555     * @return true if succeeds, false otherwise.
556     */
557    public boolean sendCurrentNetworkEapSimGsmAuthFailure() {
558        if (mCurrentNetwork == null) return false;
559        return mCurrentNetwork.sendNetworkEapSimGsmAuthFailure();
560    }
561
562    /**
563     * Send the eap sim umts auth response for the currently configured network.
564     *
565     * @param paramsStr String to send.
566     * @return true if succeeds, false otherwise.
567     */
568    public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) {
569        if (mCurrentNetwork == null) return false;
570        return mCurrentNetwork.sendNetworkEapSimUmtsAuthResponse(paramsStr);
571    }
572
573    /**
574     * Send the eap sim umts auts response for the currently configured network.
575     *
576     * @param paramsStr String to send.
577     * @return true if succeeds, false otherwise.
578     */
579    public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) {
580        if (mCurrentNetwork == null) return false;
581        return mCurrentNetwork.sendNetworkEapSimUmtsAutsResponse(paramsStr);
582    }
583
584    /**
585     * Send the eap sim umts auth failure for the currently configured network.
586     *
587     * @return true if succeeds, false otherwise.
588     */
589    public boolean sendCurrentNetworkEapSimUmtsAuthFailure() {
590        if (mCurrentNetwork == null) return false;
591        return mCurrentNetwork.sendNetworkEapSimUmtsAuthFailure();
592    }
593
594    /**
595     * Adds a new network.
596     *
597     * @return The ISupplicantNetwork object for the new network, or null if the call fails
598     */
599    private SupplicantStaNetworkHal addNetwork() {
600        synchronized (mLock) {
601            final String methodStr = "addNetwork";
602            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
603            Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
604            try {
605                mISupplicantStaIface.addNetwork((SupplicantStatus status,
606                        ISupplicantNetwork network) -> {
607                    if (checkStatusAndLogFailure(status, methodStr)) {
608                        newNetwork.value = network;
609                    }
610                });
611            } catch (RemoteException e) {
612                handleRemoteException(e, methodStr);
613            }
614            if (newNetwork.value != null) {
615                return getStaNetworkMockable(
616                        ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
617            } else {
618                return null;
619            }
620        }
621    }
622
623    /**
624     * Remove network from supplicant with network Id
625     *
626     * @return true if request is sent successfully, false otherwise.
627     */
628    private boolean removeNetwork(int id) {
629        synchronized (mLock) {
630            final String methodStr = "removeNetwork";
631            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
632            try {
633                SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
634                return checkStatusAndLogFailure(status, methodStr);
635            } catch (RemoteException e) {
636                handleRemoteException(e, methodStr);
637                return false;
638            }
639        }
640    }
641
642    /**
643     * Use this to mock the creation of SupplicantStaNetworkHal instance.
644     *
645     * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
646     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
647     * the call fails
648     */
649    protected SupplicantStaNetworkHal getStaNetworkMockable(
650            ISupplicantStaNetwork iSupplicantStaNetwork) {
651        SupplicantStaNetworkHal network =
652                new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
653                        mWifiMonitor);
654        if (network != null) {
655            network.enableVerboseLogging(mVerboseLoggingEnabled);
656        }
657        return network;
658    }
659
660    /**
661     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
662     * the call fails
663     */
664    private SupplicantStaNetworkHal getNetwork(int id) {
665        synchronized (mLock) {
666            final String methodStr = "getNetwork";
667            Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
668            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
669            try {
670                mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
671                        ISupplicantNetwork network) -> {
672                    if (checkStatusAndLogFailure(status, methodStr)) {
673                        gotNetwork.value = network;
674                    }
675                });
676            } catch (RemoteException e) {
677                handleRemoteException(e, methodStr);
678            }
679            if (gotNetwork.value != null) {
680                return getStaNetworkMockable(
681                        ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
682            } else {
683                return null;
684            }
685        }
686    }
687
688    /** See ISupplicantStaNetwork.hal for documentation */
689    private boolean registerCallback(ISupplicantStaIfaceCallback callback) {
690        synchronized (mLock) {
691            final String methodStr = "registerCallback";
692            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
693            try {
694                SupplicantStatus status =  mISupplicantStaIface.registerCallback(callback);
695                return checkStatusAndLogFailure(status, methodStr);
696            } catch (RemoteException e) {
697                handleRemoteException(e, methodStr);
698                return false;
699            }
700        }
701    }
702
703    /**
704     * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
705     * null if the call fails
706     */
707    private java.util.ArrayList<Integer> listNetworks() {
708        synchronized (mLock) {
709            final String methodStr = "listNetworks";
710            Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
711            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
712            try {
713                mISupplicantStaIface.listNetworks((SupplicantStatus status,
714                        java.util.ArrayList<Integer> networkIds) -> {
715                    if (checkStatusAndLogFailure(status, methodStr)) {
716                        networkIdList.value = networkIds;
717                    }
718                });
719            } catch (RemoteException e) {
720                handleRemoteException(e, methodStr);
721            }
722            return networkIdList.value;
723        }
724    }
725
726    /**
727     * Set WPS device name.
728     *
729     * @param name String to be set.
730     * @return true if request is sent successfully, false otherwise.
731     */
732    public boolean setWpsDeviceName(String name) {
733        synchronized (mLock) {
734            final String methodStr = "setWpsDeviceName";
735            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
736            try {
737                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceName(name);
738                return checkStatusAndLogFailure(status, methodStr);
739            } catch (RemoteException e) {
740                handleRemoteException(e, methodStr);
741                return false;
742            }
743        }
744    }
745
746    /**
747     * Set WPS device type.
748     *
749     * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
750     * @return true if request is sent successfully, false otherwise.
751     */
752    public boolean setWpsDeviceType(String typeStr) {
753        Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
754        if (!match.find() || match.groupCount() != 3) {
755            Log.e(TAG, "Malformed WPS device type " + typeStr);
756            return false;
757        }
758        short categ = Short.parseShort(match.group(1));
759        byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
760        short subCateg = Short.parseShort(match.group(3));
761
762        byte[] bytes = new byte[8];
763        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
764        byteBuffer.putShort(categ);
765        byteBuffer.put(oui);
766        byteBuffer.putShort(subCateg);
767        return setWpsDeviceType(bytes);
768    }
769
770    private boolean setWpsDeviceType(byte[/* 8 */] type) {
771        synchronized (mLock) {
772            final String methodStr = "setWpsDeviceType";
773            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
774            try {
775                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceType(type);
776                return checkStatusAndLogFailure(status, methodStr);
777            } catch (RemoteException e) {
778                handleRemoteException(e, methodStr);
779                return false;
780            }
781        }
782    }
783
784    /**
785     * Set WPS manufacturer.
786     *
787     * @param manufacturer String to be set.
788     * @return true if request is sent successfully, false otherwise.
789     */
790    public boolean setWpsManufacturer(String manufacturer) {
791        synchronized (mLock) {
792            final String methodStr = "setWpsManufacturer";
793            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
794            try {
795                SupplicantStatus status = mISupplicantStaIface.setWpsManufacturer(manufacturer);
796                return checkStatusAndLogFailure(status, methodStr);
797            } catch (RemoteException e) {
798                handleRemoteException(e, methodStr);
799                return false;
800            }
801        }
802    }
803
804    /**
805     * Set WPS model name.
806     *
807     * @param modelName String to be set.
808     * @return true if request is sent successfully, false otherwise.
809     */
810    public boolean setWpsModelName(String modelName) {
811        synchronized (mLock) {
812            final String methodStr = "setWpsModelName";
813            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
814            try {
815                SupplicantStatus status = mISupplicantStaIface.setWpsModelName(modelName);
816                return checkStatusAndLogFailure(status, methodStr);
817            } catch (RemoteException e) {
818                handleRemoteException(e, methodStr);
819                return false;
820            }
821        }
822    }
823
824    /**
825     * Set WPS model number.
826     *
827     * @param modelNumber String to be set.
828     * @return true if request is sent successfully, false otherwise.
829     */
830    public boolean setWpsModelNumber(String modelNumber) {
831        synchronized (mLock) {
832            final String methodStr = "setWpsModelNumber";
833            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
834            try {
835                SupplicantStatus status = mISupplicantStaIface.setWpsModelNumber(modelNumber);
836                return checkStatusAndLogFailure(status, methodStr);
837            } catch (RemoteException e) {
838                handleRemoteException(e, methodStr);
839                return false;
840            }
841        }
842    }
843
844    /**
845     * Set WPS serial number.
846     *
847     * @param serialNumber String to be set.
848     * @return true if request is sent successfully, false otherwise.
849     */
850    public boolean setWpsSerialNumber(String serialNumber) {
851        synchronized (mLock) {
852            final String methodStr = "setWpsSerialNumber";
853            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
854            try {
855                SupplicantStatus status = mISupplicantStaIface.setWpsSerialNumber(serialNumber);
856                return checkStatusAndLogFailure(status, methodStr);
857            } catch (RemoteException e) {
858                handleRemoteException(e, methodStr);
859                return false;
860            }
861        }
862    }
863
864    /**
865     * Set WPS config methods
866     *
867     * @param configMethodsStr List of config methods.
868     * @return true if request is sent successfully, false otherwise.
869     */
870    public boolean setWpsConfigMethods(String configMethodsStr) {
871        short configMethodsMask = 0;
872        String[] configMethodsStrArr = configMethodsStr.split("\\s+");
873        for (int i = 0; i < configMethodsStrArr.length; i++) {
874            configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
875        }
876        return setWpsConfigMethods(configMethodsMask);
877    }
878
879    private boolean setWpsConfigMethods(short configMethods) {
880        synchronized (mLock) {
881            final String methodStr = "setWpsConfigMethods";
882            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
883            try {
884                SupplicantStatus status = mISupplicantStaIface.setWpsConfigMethods(configMethods);
885                return checkStatusAndLogFailure(status, methodStr);
886            } catch (RemoteException e) {
887                handleRemoteException(e, methodStr);
888                return false;
889            }
890        }
891    }
892
893    /**
894     * Trigger a reassociation even if the iface is currently connected.
895     *
896     * @return true if request is sent successfully, false otherwise.
897     */
898    public boolean reassociate() {
899        synchronized (mLock) {
900            final String methodStr = "reassociate";
901            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
902            try {
903                SupplicantStatus status = mISupplicantStaIface.reassociate();
904                return checkStatusAndLogFailure(status, methodStr);
905            } catch (RemoteException e) {
906                handleRemoteException(e, methodStr);
907                return false;
908            }
909        }
910    }
911
912    /**
913     * Trigger a reconnection if the iface is disconnected.
914     *
915     * @return true if request is sent successfully, false otherwise.
916     */
917    public boolean reconnect() {
918        synchronized (mLock) {
919            final String methodStr = "reconnect";
920            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
921            try {
922                SupplicantStatus status = mISupplicantStaIface.reconnect();
923                return checkStatusAndLogFailure(status, methodStr);
924            } catch (RemoteException e) {
925                handleRemoteException(e, methodStr);
926                return false;
927            }
928        }
929    }
930
931    /**
932     * Trigger a disconnection from the currently connected network.
933     *
934     * @return true if request is sent successfully, false otherwise.
935     */
936    public boolean disconnect() {
937        synchronized (mLock) {
938            final String methodStr = "disconnect";
939            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
940            try {
941                SupplicantStatus status = mISupplicantStaIface.disconnect();
942                return checkStatusAndLogFailure(status, methodStr);
943            } catch (RemoteException e) {
944                handleRemoteException(e, methodStr);
945                return false;
946            }
947        }
948    }
949
950    /**
951     * Enable or disable power save mode.
952     *
953     * @param enable true to enable, false to disable.
954     * @return true if request is sent successfully, false otherwise.
955     */
956    public boolean setPowerSave(boolean enable) {
957        synchronized (mLock) {
958            final String methodStr = "setPowerSave";
959            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
960            try {
961                SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
962                return checkStatusAndLogFailure(status, methodStr);
963            } catch (RemoteException e) {
964                handleRemoteException(e, methodStr);
965                return false;
966            }
967        }
968    }
969
970    /**
971     * Initiate TDLS discover with the specified AP.
972     *
973     * @param macAddress MAC Address of the AP.
974     * @return true if request is sent successfully, false otherwise.
975     */
976    public boolean initiateTdlsDiscover(String macAddress) {
977        return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
978    }
979    /** See ISupplicantStaIface.hal for documentation */
980    private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
981        synchronized (mLock) {
982            final String methodStr = "initiateTdlsDiscover";
983            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
984            try {
985                SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
986                return checkStatusAndLogFailure(status, methodStr);
987            } catch (RemoteException e) {
988                handleRemoteException(e, methodStr);
989                return false;
990            }
991        }
992    }
993
994    /**
995     * Initiate TDLS setup with the specified AP.
996     *
997     * @param macAddress MAC Address of the AP.
998     * @return true if request is sent successfully, false otherwise.
999     */
1000    public boolean initiateTdlsSetup(String macAddress) {
1001        return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
1002    }
1003    /** See ISupplicantStaIface.hal for documentation */
1004    private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
1005        synchronized (mLock) {
1006            final String methodStr = "initiateTdlsSetup";
1007            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1008            try {
1009                SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
1010                return checkStatusAndLogFailure(status, methodStr);
1011            } catch (RemoteException e) {
1012                handleRemoteException(e, methodStr);
1013                return false;
1014            }
1015        }
1016    }
1017
1018    /**
1019     * Initiate TDLS teardown with the specified AP.
1020     * @param macAddress MAC Address of the AP.
1021     * @return true if request is sent successfully, false otherwise.
1022     */
1023    public boolean initiateTdlsTeardown(String macAddress) {
1024        return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
1025    }
1026
1027    /** See ISupplicantStaIface.hal for documentation */
1028    private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
1029        synchronized (mLock) {
1030            final String methodStr = "initiateTdlsTeardown";
1031            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1032            try {
1033                SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
1034                return checkStatusAndLogFailure(status, methodStr);
1035            } catch (RemoteException e) {
1036                handleRemoteException(e, methodStr);
1037                return false;
1038            }
1039        }
1040    }
1041
1042    /**
1043     * Request the specified ANQP elements |elements| from the specified AP |bssid|.
1044     *
1045     * @param bssid BSSID of the AP
1046     * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
1047     * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
1048     * @return true if request is sent successfully, false otherwise.
1049     */
1050    public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
1051                                     ArrayList<Integer> hs20SubTypes) {
1052        return initiateAnqpQuery(
1053                NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
1054    }
1055
1056    /** See ISupplicantStaIface.hal for documentation */
1057    private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
1058            java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
1059        synchronized (mLock) {
1060            final String methodStr = "initiateAnqpQuery";
1061            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1062            try {
1063                SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
1064                        infoElements, subTypes);
1065                return checkStatusAndLogFailure(status, methodStr);
1066            } catch (RemoteException e) {
1067                handleRemoteException(e, methodStr);
1068                return false;
1069            }
1070        }
1071    }
1072
1073    /**
1074     * Request the specified ANQP ICON from the specified AP |bssid|.
1075     *
1076     * @param bssid BSSID of the AP
1077     * @param fileName Name of the file to request.
1078     * @return true if request is sent successfully, false otherwise.
1079     */
1080    public boolean initiateHs20IconQuery(String bssid, String fileName) {
1081        return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
1082    }
1083
1084    /** See ISupplicantStaIface.hal for documentation */
1085    private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
1086        synchronized (mLock) {
1087            final String methodStr = "initiateHs20IconQuery";
1088            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1089            try {
1090                SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
1091                        fileName);
1092                return checkStatusAndLogFailure(status, methodStr);
1093            } catch (RemoteException e) {
1094                handleRemoteException(e, methodStr);
1095                return false;
1096            }
1097        }
1098    }
1099
1100    /**
1101     * Makes a callback to HIDL to getMacAddress from supplicant
1102     *
1103     * @return string containing the MAC address, or null on a failed call
1104     */
1105    public String getMacAddress() {
1106        synchronized (mLock) {
1107            final String methodStr = "getMacAddress";
1108            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
1109            Mutable<String> gotMac = new Mutable<>();
1110            try {
1111                mISupplicantStaIface.getMacAddress((SupplicantStatus status,
1112                        byte[/* 6 */] macAddr) -> {
1113                    if (checkStatusAndLogFailure(status, methodStr)) {
1114                        gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
1115                    }
1116                });
1117            } catch (RemoteException e) {
1118                handleRemoteException(e, methodStr);
1119            }
1120            return gotMac.value;
1121        }
1122    }
1123
1124    /**
1125     * Start using the added RX filters.
1126     *
1127     * @return true if request is sent successfully, false otherwise.
1128     */
1129    public boolean startRxFilter() {
1130        synchronized (mLock) {
1131            final String methodStr = "startRxFilter";
1132            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1133            try {
1134                SupplicantStatus status = mISupplicantStaIface.startRxFilter();
1135                return checkStatusAndLogFailure(status, methodStr);
1136            } catch (RemoteException e) {
1137                handleRemoteException(e, methodStr);
1138                return false;
1139            }
1140        }
1141    }
1142
1143    /**
1144     * Stop using the added RX filters.
1145     *
1146     * @return true if request is sent successfully, false otherwise.
1147     */
1148    public boolean stopRxFilter() {
1149        synchronized (mLock) {
1150            final String methodStr = "stopRxFilter";
1151            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1152            try {
1153                SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
1154                return checkStatusAndLogFailure(status, methodStr);
1155            } catch (RemoteException e) {
1156                handleRemoteException(e, methodStr);
1157                return false;
1158            }
1159        }
1160    }
1161
1162    public static final byte RX_FILTER_TYPE_V4_MULTICAST =
1163            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1164    public static final byte RX_FILTER_TYPE_V6_MULTICAST =
1165            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1166    /**
1167     * Add an RX filter.
1168     *
1169     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
1170     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
1171     * @return true if request is sent successfully, false otherwise.
1172     */
1173    public boolean addRxFilter(byte type) {
1174        synchronized (mLock) {
1175            final String methodStr = "addRxFilter";
1176            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1177            try {
1178                SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
1179                return checkStatusAndLogFailure(status, methodStr);
1180            } catch (RemoteException e) {
1181                handleRemoteException(e, methodStr);
1182                return false;
1183            }
1184        }
1185    }
1186
1187    /**
1188     * Remove an RX filter.
1189     *
1190     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
1191     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
1192     * @return true if request is sent successfully, false otherwise.
1193     */
1194    public boolean removeRxFilter(byte type) {
1195        synchronized (mLock) {
1196            final String methodStr = "removeRxFilter";
1197            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1198            try {
1199                SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
1200                return checkStatusAndLogFailure(status, methodStr);
1201            } catch (RemoteException e) {
1202                handleRemoteException(e, methodStr);
1203                return false;
1204            }
1205        }
1206    }
1207
1208    public static final byte BT_COEX_MODE_ENABLED = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
1209    public static final byte BT_COEX_MODE_DISABLED = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
1210    public static final byte BT_COEX_MODE_SENSE = ISupplicantStaIface.BtCoexistenceMode.SENSE;
1211    /**
1212     * Set Bt co existense mode.
1213     *
1214     * @param mode one of the above {@link #BT_COEX_MODE_ENABLED}, {@link #BT_COEX_MODE_DISABLED}
1215     *             or {@link #BT_COEX_MODE_SENSE} values.
1216     * @return true if request is sent successfully, false otherwise.
1217     */
1218    public boolean setBtCoexistenceMode(byte mode) {
1219        synchronized (mLock) {
1220            final String methodStr = "setBtCoexistenceMode";
1221            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1222            try {
1223                SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
1224                return checkStatusAndLogFailure(status, methodStr);
1225            } catch (RemoteException e) {
1226                handleRemoteException(e, methodStr);
1227                return false;
1228            }
1229        }
1230    }
1231
1232    /** Enable or disable BT coexistence mode.
1233     *
1234     * @param enable true to enable, false to disable.
1235     * @return true if request is sent successfully, false otherwise.
1236     */
1237    public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
1238        synchronized (mLock) {
1239            final String methodStr = "setBtCoexistenceScanModeEnabled";
1240            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1241            try {
1242                SupplicantStatus status =
1243                        mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
1244                return checkStatusAndLogFailure(status, methodStr);
1245            } catch (RemoteException e) {
1246                handleRemoteException(e, methodStr);
1247                return false;
1248            }
1249        }
1250    }
1251
1252    /**
1253     * Enable or disable suspend mode optimizations.
1254     *
1255     * @param enable true to enable, false otherwise.
1256     * @return true if request is sent successfully, false otherwise.
1257     */
1258    public boolean setSuspendModeEnabled(boolean enable) {
1259        synchronized (mLock) {
1260            final String methodStr = "setSuspendModeEnabled";
1261            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1262            try {
1263                SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
1264                return checkStatusAndLogFailure(status, methodStr);
1265            } catch (RemoteException e) {
1266                handleRemoteException(e, methodStr);
1267                return false;
1268            }
1269        }
1270    }
1271
1272    /**
1273     * Set country code.
1274     *
1275     * @param codeStr 2 byte ASCII string. For ex: US, CA.
1276     * @return true if request is sent successfully, false otherwise.
1277     */
1278    public boolean setCountryCode(String codeStr) {
1279        return setCountryCode(NativeUtil.stringToByteArray(codeStr));
1280    }
1281
1282    /** See ISupplicantStaIface.hal for documentation */
1283    private boolean setCountryCode(byte[/* 2 */] code) {
1284        synchronized (mLock) {
1285            final String methodStr = "setCountryCode";
1286            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1287            try {
1288                SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
1289                return checkStatusAndLogFailure(status, methodStr);
1290            } catch (RemoteException e) {
1291                handleRemoteException(e, methodStr);
1292                return false;
1293            }
1294        }
1295    }
1296
1297    /**
1298     * Start WPS pin registrar operation with the specified peer and pin.
1299     *
1300     * @param bssidStr BSSID of the peer.
1301     * @param pin Pin to be used.
1302     * @return true if request is sent successfully, false otherwise.
1303     */
1304    public boolean startWpsRegistrar(String bssidStr, String pin) {
1305        return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
1306    }
1307
1308    /** See ISupplicantStaIface.hal for documentation */
1309    private boolean startWpsRegistrar(byte[/* 6 */] bssid, String pin) {
1310        synchronized (mLock) {
1311            final String methodStr = "startWpsRegistrar";
1312            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1313            try {
1314                SupplicantStatus status = mISupplicantStaIface.startWpsRegistrar(bssid, pin);
1315                return checkStatusAndLogFailure(status, methodStr);
1316            } catch (RemoteException e) {
1317                handleRemoteException(e, methodStr);
1318                return false;
1319            }
1320        }
1321    }
1322
1323    /**
1324     * Start WPS pin display operation with the specified peer.
1325     *
1326     * @param bssidStr BSSID of the peer.
1327     * @return true if request is sent successfully, false otherwise.
1328     */
1329    public boolean startWpsPbc(String bssidStr) {
1330        return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
1331    }
1332
1333    /** See ISupplicantStaIface.hal for documentation */
1334    private boolean startWpsPbc(byte[/* 6 */] bssid) {
1335        synchronized (mLock) {
1336            final String methodStr = "startWpsPbc";
1337            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1338            try {
1339                SupplicantStatus status = mISupplicantStaIface.startWpsPbc(bssid);
1340                return checkStatusAndLogFailure(status, methodStr);
1341            } catch (RemoteException e) {
1342                handleRemoteException(e, methodStr);
1343                return false;
1344            }
1345        }
1346    }
1347
1348    /**
1349     * Start WPS pin keypad operation with the specified pin.
1350     *
1351     * @param pin Pin to be used.
1352     * @return true if request is sent successfully, false otherwise.
1353     */
1354    public boolean startWpsPinKeypad(String pin) {
1355        synchronized (mLock) {
1356            final String methodStr = "startWpsPinKeypad";
1357            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1358            try {
1359                SupplicantStatus status = mISupplicantStaIface.startWpsPinKeypad(pin);
1360                return checkStatusAndLogFailure(status, methodStr);
1361            } catch (RemoteException e) {
1362                handleRemoteException(e, methodStr);
1363                return false;
1364            }
1365        }
1366    }
1367
1368    /**
1369     * Start WPS pin display operation with the specified peer.
1370     *
1371     * @param bssidStr BSSID of the peer.
1372     * @return new pin generated on success, null otherwise.
1373     */
1374    public String startWpsPinDisplay(String bssidStr) {
1375        return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
1376    }
1377
1378    /** See ISupplicantStaIface.hal for documentation */
1379    private String startWpsPinDisplay(byte[/* 6 */] bssid) {
1380        synchronized (mLock) {
1381            final String methodStr = "startWpsPinDisplay";
1382            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
1383            final Mutable<String> gotPin = new Mutable<>();
1384            try {
1385                mISupplicantStaIface.startWpsPinDisplay(bssid,
1386                        (SupplicantStatus status, String pin) -> {
1387                            if (checkStatusAndLogFailure(status, methodStr)) {
1388                                gotPin.value = pin;
1389                            }
1390                        });
1391            } catch (RemoteException e) {
1392                handleRemoteException(e, methodStr);
1393            }
1394            return gotPin.value;
1395        }
1396    }
1397
1398    /**
1399     * Cancels any ongoing WPS requests.
1400     *
1401     * @return true if request is sent successfully, false otherwise.
1402     */
1403    public boolean cancelWps() {
1404        synchronized (mLock) {
1405            final String methodStr = "cancelWps";
1406            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1407            try {
1408                SupplicantStatus status = mISupplicantStaIface.cancelWps();
1409                return checkStatusAndLogFailure(status, methodStr);
1410            } catch (RemoteException e) {
1411                handleRemoteException(e, methodStr);
1412                return false;
1413            }
1414        }
1415    }
1416
1417    /**
1418     * Sets whether to use external sim for SIM/USIM processing.
1419     *
1420     * @param useExternalSim true to enable, false otherwise.
1421     * @return true if request is sent successfully, false otherwise.
1422     */
1423    public boolean setExternalSim(boolean useExternalSim) {
1424        synchronized (mLock) {
1425            final String methodStr = "setExternalSim";
1426            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1427            try {
1428                SupplicantStatus status = mISupplicantStaIface.setExternalSim(useExternalSim);
1429                return checkStatusAndLogFailure(status, methodStr);
1430            } catch (RemoteException e) {
1431                handleRemoteException(e, methodStr);
1432                return false;
1433            }
1434        }
1435    }
1436
1437    public static final int LOG_LEVEL_EXCESSIVE = ISupplicant.DebugLevel.EXCESSIVE;
1438    public static final int LOG_LEVEL_MSGDUMP = ISupplicant.DebugLevel.MSGDUMP;
1439    public static final int LOG_LEVEL_DEBUG = ISupplicant.DebugLevel.DEBUG;
1440    public static final int LOG_LEVEL_INFO = ISupplicant.DebugLevel.INFO;
1441    public static final int LOG_LEVEL_WARNING = ISupplicant.DebugLevel.WARNING;
1442    public static final int LOG_LEVEL_ERROR = ISupplicant.DebugLevel.ERROR;
1443    /**
1444     * Set the debug log level for wpa_supplicant
1445     * @param level One of the above {@link #LOG_LEVEL_EXCESSIVE} - {@link #LOG_LEVEL_ERROR} value.
1446     * @return true if request is sent successfully, false otherwise.
1447     */
1448    public boolean setLogLevel(int level) {
1449        return setDebugParams(level, false, false);
1450    }
1451
1452    /** See ISupplicant.hal for documentation */
1453    private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
1454        synchronized (mLock) {
1455            final String methodStr = "setDebugParams";
1456            if (!checkSupplicantAndLogFailure(methodStr)) return false;
1457            try {
1458                SupplicantStatus status =
1459                        mISupplicant.setDebugParams(level, showTimestamp, showKeys);
1460                return checkStatusAndLogFailure(status, methodStr);
1461            } catch (RemoteException e) {
1462                handleRemoteException(e, methodStr);
1463                return false;
1464            }
1465        }
1466    }
1467
1468    /**
1469     * Set concurrency priority between P2P & STA operations.
1470     *
1471     * @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
1472     *                            false otherwise.
1473     * @return true if request is sent successfully, false otherwise.
1474     */
1475    public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
1476        if (isStaHigherPriority) {
1477            return setConcurrencyPriority(IfaceType.STA);
1478        } else {
1479            return setConcurrencyPriority(IfaceType.P2P);
1480        }
1481    }
1482
1483    /** See ISupplicant.hal for documentation */
1484    private boolean setConcurrencyPriority(int type) {
1485        synchronized (mLock) {
1486            final String methodStr = "setConcurrencyPriority";
1487            if (!checkSupplicantAndLogFailure(methodStr)) return false;
1488            try {
1489                SupplicantStatus status = mISupplicant.setConcurrencyPriority(type);
1490                return checkStatusAndLogFailure(status, methodStr);
1491            } catch (RemoteException e) {
1492                handleRemoteException(e, methodStr);
1493                return false;
1494            }
1495        }
1496    }
1497
1498    /**
1499     * Returns false if Supplicant is null, and logs failure to call methodStr
1500     */
1501    private boolean checkSupplicantAndLogFailure(final String methodStr) {
1502        if (mISupplicant == null) {
1503            Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
1504            return false;
1505        }
1506        return true;
1507    }
1508
1509    /**
1510     * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
1511     */
1512    private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
1513        if (mISupplicantStaIface == null) {
1514            Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
1515            return false;
1516        }
1517        return true;
1518    }
1519
1520    /**
1521     * Returns true if provided status code is SUCCESS, logs debug message and returns false
1522     * otherwise
1523     */
1524    private boolean checkStatusAndLogFailure(SupplicantStatus status,
1525            final String methodStr) {
1526        if (status.code != SupplicantStatusCode.SUCCESS) {
1527            Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: "
1528                    + supplicantStatusCodeToString(status.code) + ", " + status.debugMessage);
1529            return false;
1530        } else {
1531            if (mVerboseLoggingEnabled) {
1532                Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
1533            }
1534            return true;
1535        }
1536    }
1537
1538    /**
1539     * Helper function to log callbacks.
1540     */
1541    private void logCallback(final String methodStr) {
1542        if (mVerboseLoggingEnabled) {
1543            Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
1544        }
1545    }
1546
1547
1548    private void handleRemoteException(RemoteException e, String methodStr) {
1549        supplicantServiceDiedHandler();
1550        Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
1551    }
1552
1553    /**
1554     * Converts SupplicantStatus code values to strings for debug logging
1555     * TODO(b/34811152) Remove this, or make it more break resistance
1556     */
1557    public static String supplicantStatusCodeToString(int code) {
1558        switch (code) {
1559            case 0:
1560                return "SUCCESS";
1561            case 1:
1562                return "FAILURE_UNKNOWN";
1563            case 2:
1564                return "FAILURE_ARGS_INVALID";
1565            case 3:
1566                return "FAILURE_IFACE_INVALID";
1567            case 4:
1568                return "FAILURE_IFACE_UNKNOWN";
1569            case 5:
1570                return "FAILURE_IFACE_EXISTS";
1571            case 6:
1572                return "FAILURE_IFACE_DISABLED";
1573            case 7:
1574                return "FAILURE_IFACE_NOT_DISCONNECTED";
1575            case 8:
1576                return "FAILURE_NETWORK_INVALID";
1577            case 9:
1578                return "FAILURE_NETWORK_UNKNOWN";
1579            default:
1580                return "??? UNKNOWN_CODE";
1581        }
1582    }
1583
1584
1585    /**
1586     * Converts the Wps config method string to the equivalent enum value.
1587     */
1588    private static short stringToWpsConfigMethod(String configMethod) {
1589        switch (configMethod) {
1590            case "usba":
1591                return WpsConfigMethods.USBA;
1592            case "ethernet":
1593                return WpsConfigMethods.ETHERNET;
1594            case "label":
1595                return WpsConfigMethods.LABEL;
1596            case "display":
1597                return WpsConfigMethods.DISPLAY;
1598            case "int_nfc_token":
1599                return WpsConfigMethods.INT_NFC_TOKEN;
1600            case "ext_nfc_token":
1601                return WpsConfigMethods.EXT_NFC_TOKEN;
1602            case "nfc_interface":
1603                return WpsConfigMethods.NFC_INTERFACE;
1604            case "push_button":
1605                return WpsConfigMethods.PUSHBUTTON;
1606            case "keypad":
1607                return WpsConfigMethods.KEYPAD;
1608            case "virtual_push_button":
1609                return WpsConfigMethods.VIRT_PUSHBUTTON;
1610            case "physical_push_button":
1611                return WpsConfigMethods.PHY_PUSHBUTTON;
1612            case "p2ps":
1613                return WpsConfigMethods.P2PS;
1614            case "virtual_display":
1615                return WpsConfigMethods.VIRT_DISPLAY;
1616            case "physical_display":
1617                return WpsConfigMethods.PHY_DISPLAY;
1618            default:
1619                throw new IllegalArgumentException(
1620                        "Invalid WPS config method: " + configMethod);
1621        }
1622    }
1623
1624    /**
1625     * Converts the supplicant state received from HIDL to the equivalent framework state.
1626     */
1627    private static SupplicantState supplicantHidlStateToFrameworkState(int state) {
1628        switch (state) {
1629            case ISupplicantStaIfaceCallback.State.DISCONNECTED:
1630                return SupplicantState.DISCONNECTED;
1631            case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
1632                return SupplicantState.INTERFACE_DISABLED;
1633            case ISupplicantStaIfaceCallback.State.INACTIVE:
1634                return SupplicantState.INACTIVE;
1635            case ISupplicantStaIfaceCallback.State.SCANNING:
1636                return SupplicantState.SCANNING;
1637            case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
1638                return SupplicantState.AUTHENTICATING;
1639            case ISupplicantStaIfaceCallback.State.ASSOCIATING:
1640                return SupplicantState.ASSOCIATING;
1641            case ISupplicantStaIfaceCallback.State.ASSOCIATED:
1642                return SupplicantState.ASSOCIATED;
1643            case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
1644                return SupplicantState.FOUR_WAY_HANDSHAKE;
1645            case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
1646                return SupplicantState.GROUP_HANDSHAKE;
1647            case ISupplicantStaIfaceCallback.State.COMPLETED:
1648                return SupplicantState.COMPLETED;
1649            default:
1650                throw new IllegalArgumentException("Invalid state: " + state);
1651        }
1652    }
1653
1654    private static class Mutable<E> {
1655        public E value;
1656
1657        Mutable() {
1658            value = null;
1659        }
1660
1661        Mutable(E value) {
1662            this.value = value;
1663        }
1664    }
1665
1666    private class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub {
1667        /**
1668         * Parses the provided payload into an ANQP element.
1669         *
1670         * @param infoID  Element type.
1671         * @param payload Raw payload bytes.
1672         * @return AnqpElement instance on success, null on failure.
1673         */
1674        private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
1675                                             ArrayList<Byte> payload) {
1676            try {
1677                return Constants.getANQPElementID(infoID) != null
1678                        ? ANQPParser.parseElement(
1679                        infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
1680                        : ANQPParser.parseHS20Element(
1681                        infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
1682            } catch (IOException | BufferUnderflowException e) {
1683                Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
1684                return null;
1685            }
1686        }
1687
1688        /**
1689         * Parse the ANQP element data and add to the provided elements map if successful.
1690         *
1691         * @param elementsMap Map to add the parsed out element to.
1692         * @param infoID  Element type.
1693         * @param payload Raw payload bytes.
1694         */
1695        private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
1696                                         Constants.ANQPElementType infoID,
1697                                         ArrayList<Byte> payload) {
1698            if (payload == null || payload.isEmpty()) return;
1699            ANQPElement element = parseAnqpElement(infoID, payload);
1700            if (element != null) {
1701                elementsMap.put(infoID, element);
1702            }
1703        }
1704
1705        /**
1706         * Helper utility to convert the bssid bytes to long.
1707         */
1708        private Long toLongBssid(byte[] bssidBytes) {
1709            try {
1710                return ByteBufferReader.readInteger(
1711                        ByteBuffer.wrap(bssidBytes), ByteOrder.BIG_ENDIAN, bssidBytes.length);
1712            } catch (BufferUnderflowException | IllegalArgumentException e) {
1713                return 0L;
1714            }
1715        }
1716
1717        @Override
1718        public void onNetworkAdded(int id) {
1719            logCallback("onNetworkAdded");
1720        }
1721
1722        @Override
1723        public void onNetworkRemoved(int id) {
1724            logCallback("onNetworkRemoved");
1725        }
1726
1727        @Override
1728        public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
1729                                   ArrayList<Byte> ssid) {
1730            logCallback("onStateChanged");
1731            synchronized (mLock) {
1732                SupplicantState newSupplicantState = supplicantHidlStateToFrameworkState(newState);
1733                WifiSsid wifiSsid =
1734                        WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
1735                String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
1736                mWifiMonitor.broadcastSupplicantStateChangeEvent(
1737                        mIfaceName, mFrameworkNetworkId, wifiSsid, bssidStr, newSupplicantState);
1738                if (newSupplicantState == SupplicantState.ASSOCIATED) {
1739                    mWifiMonitor.broadcastAssociationSuccesfulEvent(mIfaceName, bssidStr);
1740                } else if (newSupplicantState == SupplicantState.COMPLETED) {
1741                    mWifiMonitor.broadcastNetworkConnectionEvent(
1742                            mIfaceName, mFrameworkNetworkId, bssidStr);
1743                }
1744            }
1745        }
1746
1747        @Override
1748        public void onAnqpQueryDone(byte[/* 6 */] bssid,
1749                                    ISupplicantStaIfaceCallback.AnqpData data,
1750                                    ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
1751            logCallback("onAnqpQueryDone");
1752            synchronized (mLock) {
1753                Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
1754                addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
1755                addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
1756                addAnqpElementToMap(
1757                        elementsMap, ANQPIPAddrAvailability, data.ipAddrTypeAvailability);
1758                addAnqpElementToMap(elementsMap, ANQPNAIRealm, data.naiRealm);
1759                addAnqpElementToMap(elementsMap, ANQP3GPPNetwork, data.anqp3gppCellularNetwork);
1760                addAnqpElementToMap(elementsMap, ANQPDomName, data.domainName);
1761                addAnqpElementToMap(elementsMap, HSFriendlyName, hs20Data.operatorFriendlyName);
1762                addAnqpElementToMap(elementsMap, HSWANMetrics, hs20Data.wanMetrics);
1763                addAnqpElementToMap(elementsMap, HSConnCapability, hs20Data.connectionCapability);
1764                addAnqpElementToMap(elementsMap, HSOSUProviders, hs20Data.osuProvidersList);
1765                mWifiMonitor.broadcastAnqpDoneEvent(
1766                        mIfaceName, new AnqpEvent(toLongBssid(bssid), elementsMap));
1767            }
1768        }
1769
1770        @Override
1771        public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
1772                                        ArrayList<Byte> data) {
1773            logCallback("onHs20IconQueryDone");
1774            synchronized (mLock) {
1775                mWifiMonitor.broadcastIconDoneEvent(
1776                        mIfaceName,
1777                        new IconEvent(toLongBssid(bssid), fileName, data.size(),
1778                                NativeUtil.byteArrayFromArrayList(data)));
1779            }
1780        }
1781
1782        @Override
1783        public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
1784            logCallback("onHs20SubscriptionRemediation");
1785            synchronized (mLock) {
1786                mWifiMonitor.broadcastWnmEvent(
1787                        mIfaceName, new WnmData(toLongBssid(bssid), url, osuMethod));
1788            }
1789        }
1790
1791        @Override
1792        public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
1793                                               int reAuthDelayInSec, String url) {
1794            logCallback("onHs20DeauthImminentNotice");
1795            synchronized (mLock) {
1796                mWifiMonitor.broadcastWnmEvent(
1797                        mIfaceName,
1798                        new WnmData(toLongBssid(bssid), url, reasonCode == WnmData.ESS,
1799                                reAuthDelayInSec));
1800            }
1801        }
1802
1803        @Override
1804        public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
1805            logCallback("onDisconnected");
1806            synchronized (mLock) {
1807                mWifiMonitor.broadcastNetworkDisconnectionEvent(
1808                        mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
1809                        NativeUtil.macAddressFromByteArray(bssid));
1810            }
1811        }
1812
1813        @Override
1814        public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode) {
1815            logCallback("onAssociationRejected");
1816            synchronized (mLock) {
1817                // TODO(b/35464954): Need to figure out when to trigger
1818                // |WifiMonitor.AUTHENTICATION_FAILURE_REASON_WRONG_PSWD|
1819                mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode,
1820                        NativeUtil.macAddressFromByteArray(bssid));
1821            }
1822        }
1823
1824        @Override
1825        public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
1826            logCallback("onAuthenticationTimeout");
1827            synchronized (mLock) {
1828                mWifiMonitor.broadcastAuthenticationFailureEvent(
1829                        mIfaceName, WifiMonitor.AUTHENTICATION_FAILURE_REASON_TIMEOUT);
1830            }
1831        }
1832
1833        @Override
1834        public void onEapFailure() {
1835            logCallback("onEapFailure");
1836            synchronized (mLock) {
1837                mWifiMonitor.broadcastAuthenticationFailureEvent(
1838                        mIfaceName, WifiMonitor.AUTHENTICATION_FAILURE_REASON_EAP_FAILURE);
1839            }
1840        }
1841
1842        @Override
1843        public void onWpsEventSuccess() {
1844            logCallback("onWpsEventSuccess");
1845            synchronized (mLock) {
1846                mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName);
1847            }
1848        }
1849
1850        @Override
1851        public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
1852            logCallback("onWpsEventFail");
1853            synchronized (mLock) {
1854                if (configError == WpsConfigError.MSG_TIMEOUT
1855                        && errorInd == WpsErrorIndication.NO_ERROR) {
1856                    mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
1857                } else {
1858                    mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd);
1859                }
1860            }
1861        }
1862
1863        @Override
1864        public void onWpsEventPbcOverlap() {
1865            logCallback("onWpsEventPbcOverlap");
1866            synchronized (mLock) {
1867                mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
1868            }
1869        }
1870
1871        @Override
1872        public void onExtRadioWorkStart(int id) {
1873            logCallback("onExtRadioWorkStart");
1874        }
1875
1876        @Override
1877        public void onExtRadioWorkTimeout(int id) {
1878            logCallback("onExtRadioWorkTimeout");
1879        }
1880    }
1881
1882    private void logd(String s) {
1883        Log.d(TAG, s);
1884    }
1885
1886    private void logi(String s) {
1887        Log.i(TAG, s);
1888    }
1889
1890    private void loge(String s) {
1891        Log.e(TAG, s);
1892    }
1893}
1894