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