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