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.annotation.NonNull;
30import android.content.Context;
31import android.hardware.wifi.supplicant.V1_0.ISupplicant;
32import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
33import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
34import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
35import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
36import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
37import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
38import android.hardware.wifi.supplicant.V1_0.IfaceType;
39import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
40import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
41import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
42import android.hidl.manager.V1_0.IServiceManager;
43import android.hidl.manager.V1_0.IServiceNotification;
44import android.net.IpConfiguration;
45import android.net.wifi.SupplicantState;
46import android.net.wifi.WifiConfiguration;
47import android.net.wifi.WifiManager;
48import android.net.wifi.WifiSsid;
49import android.os.HidlSupport.Mutable;
50import android.os.HwRemoteBinder;
51import android.os.RemoteException;
52import android.text.TextUtils;
53import android.util.Log;
54import android.util.Pair;
55import android.util.SparseArray;
56
57import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler;
58import com.android.server.wifi.hotspot2.AnqpEvent;
59import com.android.server.wifi.hotspot2.IconEvent;
60import com.android.server.wifi.hotspot2.WnmData;
61import com.android.server.wifi.hotspot2.anqp.ANQPElement;
62import com.android.server.wifi.hotspot2.anqp.ANQPParser;
63import com.android.server.wifi.hotspot2.anqp.Constants;
64import com.android.server.wifi.util.NativeUtil;
65
66import java.io.IOException;
67import java.nio.BufferUnderflowException;
68import java.nio.ByteBuffer;
69import java.nio.ByteOrder;
70import java.util.ArrayList;
71import java.util.HashMap;
72import java.util.List;
73import java.util.Map;
74import java.util.NoSuchElementException;
75import java.util.Objects;
76import java.util.regex.Matcher;
77import java.util.regex.Pattern;
78
79import javax.annotation.concurrent.ThreadSafe;
80
81/**
82 * Hal calls for bring up/shut down of the supplicant daemon and for
83 * sending requests to the supplicant daemon
84 * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
85 * access level) acquires mLock.
86 */
87@ThreadSafe
88public class SupplicantStaIfaceHal {
89    private static final String TAG = "SupplicantStaIfaceHal";
90    /**
91     * Regex pattern for extracting the wps device type bytes.
92     * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
93     */
94    private static final Pattern WPS_DEVICE_TYPE_PATTERN =
95            Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
96
97    private final Object mLock = new Object();
98    private boolean mVerboseLoggingEnabled = false;
99
100    // Supplicant HAL interface objects
101    private IServiceManager mIServiceManager = null;
102    private ISupplicant mISupplicant;
103    private HashMap<String, ISupplicantStaIface> mISupplicantStaIfaces = new HashMap<>();
104    private HashMap<String, ISupplicantStaIfaceCallback> mISupplicantStaIfaceCallbacks =
105            new HashMap<>();
106    private HashMap<String, SupplicantStaNetworkHal> mCurrentNetworkRemoteHandles = new HashMap<>();
107    private HashMap<String, WifiConfiguration> mCurrentNetworkLocalConfigs = new HashMap<>();
108    private SupplicantDeathEventHandler mDeathEventHandler;
109    private final Context mContext;
110    private final WifiMonitor mWifiMonitor;
111
112    private final IServiceNotification mServiceNotificationCallback =
113            new IServiceNotification.Stub() {
114        public void onRegistration(String fqName, String name, boolean preexisting) {
115            synchronized (mLock) {
116                if (mVerboseLoggingEnabled) {
117                    Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
118                            + ", " + name + " preexisting=" + preexisting);
119                }
120                if (!initSupplicantService()) {
121                    Log.e(TAG, "initalizing ISupplicant failed.");
122                    supplicantServiceDiedHandler();
123                } else {
124                    Log.i(TAG, "Completed initialization of ISupplicant.");
125                }
126            }
127        }
128    };
129    private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
130            cookie -> {
131                synchronized (mLock) {
132                    Log.w(TAG, "IServiceManager died: cookie=" + cookie);
133                    supplicantServiceDiedHandler();
134                    mIServiceManager = null; // Will need to register a new ServiceNotification
135                }
136            };
137    private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
138            cookie -> {
139                synchronized (mLock) {
140                    Log.w(TAG, "ISupplicant died: cookie=" + cookie);
141                    supplicantServiceDiedHandler();
142                }
143            };
144
145
146    public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) {
147        mContext = context;
148        mWifiMonitor = monitor;
149    }
150
151    /**
152     * Enable/Disable verbose logging.
153     *
154     * @param enable true to enable, false to disable.
155     */
156    void enableVerboseLogging(boolean enable) {
157        synchronized (mLock) {
158            mVerboseLoggingEnabled = enable;
159        }
160    }
161
162    private boolean linkToServiceManagerDeath() {
163        synchronized (mLock) {
164            if (mIServiceManager == null) return false;
165            try {
166                if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
167                    Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
168                    supplicantServiceDiedHandler();
169                    mIServiceManager = null; // Will need to register a new ServiceNotification
170                    return false;
171                }
172            } catch (RemoteException e) {
173                Log.e(TAG, "IServiceManager.linkToDeath exception", e);
174                return false;
175            }
176            return true;
177        }
178    }
179
180    /**
181     * Registers a service notification for the ISupplicant service, which triggers intialization of
182     * the ISupplicantStaIface
183     * @return true if the service notification was successfully registered
184     */
185    public boolean initialize() {
186        synchronized (mLock) {
187            if (mVerboseLoggingEnabled) {
188                Log.i(TAG, "Registering ISupplicant service ready callback.");
189            }
190            mISupplicant = null;
191            mISupplicantStaIfaces.clear();
192            if (mIServiceManager != null) {
193                // Already have an IServiceManager and serviceNotification registered, don't
194                // don't register another.
195                return true;
196            }
197            try {
198                mIServiceManager = getServiceManagerMockable();
199                if (mIServiceManager == null) {
200                    Log.e(TAG, "Failed to get HIDL Service Manager");
201                    return false;
202                }
203                if (!linkToServiceManagerDeath()) {
204                    return false;
205                }
206                /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
207                   exists */
208                if (!mIServiceManager.registerForNotifications(
209                        ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
210                    Log.e(TAG, "Failed to register for notifications to "
211                            + ISupplicant.kInterfaceName);
212                    mIServiceManager = null; // Will need to register a new ServiceNotification
213                    return false;
214                }
215            } catch (RemoteException e) {
216                Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
217                        + e);
218                supplicantServiceDiedHandler();
219            }
220            return true;
221        }
222    }
223
224    private boolean linkToSupplicantDeath() {
225        synchronized (mLock) {
226            if (mISupplicant == null) return false;
227            try {
228                if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
229                    Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
230                    supplicantServiceDiedHandler();
231                    return false;
232                }
233            } catch (RemoteException e) {
234                Log.e(TAG, "ISupplicant.linkToDeath exception", e);
235                return false;
236            }
237            return true;
238        }
239    }
240
241    private boolean initSupplicantService() {
242        synchronized (mLock) {
243            try {
244                mISupplicant = getSupplicantMockable();
245            } catch (RemoteException e) {
246                Log.e(TAG, "ISupplicant.getService exception: " + e);
247                return false;
248            }
249            if (mISupplicant == null) {
250                Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
251                return false;
252            }
253            if (!linkToSupplicantDeath()) {
254                return false;
255            }
256        }
257        return true;
258    }
259
260    private int getCurrentNetworkId(@NonNull String ifaceName) {
261        synchronized (mLock) {
262            WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
263            if (currentConfig == null) {
264                return WifiConfiguration.INVALID_NETWORK_ID;
265            }
266            return currentConfig.networkId;
267        }
268    }
269
270    /**
271     * Setup a STA interface for the specified iface name.
272     *
273     * @param ifaceName Name of the interface.
274     * @return true on success, false otherwise.
275     */
276    public boolean setupIface(@NonNull String ifaceName) {
277        final String methodStr = "setupIface";
278        if (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) != null) return false;
279        ISupplicantIface ifaceHwBinder;
280        if (isV1_1()) {
281            ifaceHwBinder = addIfaceV1_1(ifaceName);
282        } else {
283            ifaceHwBinder = getIfaceV1_0(ifaceName);
284        }
285        if (ifaceHwBinder == null) {
286            Log.e(TAG, "setupIface got null iface");
287            return false;
288        }
289        SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName);
290
291        if (isV1_1()) {
292            android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface iface =
293                getStaIfaceMockableV1_1(ifaceHwBinder);
294            SupplicantStaIfaceHalCallbackV1_1 callbackV1_1 =
295                new SupplicantStaIfaceHalCallbackV1_1(ifaceName, callback);
296
297            if (!registerCallbackV1_1(iface, callbackV1_1)) {
298                return false;
299            }
300            mISupplicantStaIfaces.put(ifaceName, iface);
301            mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV1_1);
302        } else {
303            ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder);
304
305            if (!registerCallback(iface, callback)) {
306                return false;
307            }
308            mISupplicantStaIfaces.put(ifaceName, iface);
309            mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
310        }
311        return true;
312    }
313
314    /**
315     * Get a STA interface for the specified iface name.
316     *
317     * @param ifaceName Name of the interface.
318     * @return true on success, false otherwise.
319     */
320    private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) {
321        synchronized (mLock) {
322            /** List all supplicant Ifaces */
323            final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
324            try {
325                mISupplicant.listInterfaces((SupplicantStatus status,
326                                             ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
327                    if (status.code != SupplicantStatusCode.SUCCESS) {
328                        Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
329                        return;
330                    }
331                    supplicantIfaces.addAll(ifaces);
332                });
333            } catch (RemoteException e) {
334                Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
335                handleRemoteException(e, "listInterfaces");
336                return null;
337            }
338            if (supplicantIfaces.size() == 0) {
339                Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
340                return null;
341            }
342            Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
343            for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
344                if (ifaceInfo.type == IfaceType.STA && ifaceName.equals(ifaceInfo.name)) {
345                    try {
346                        mISupplicant.getInterface(ifaceInfo,
347                                (SupplicantStatus status, ISupplicantIface iface) -> {
348                                    if (status.code != SupplicantStatusCode.SUCCESS) {
349                                        Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
350                                        return;
351                                    }
352                                    supplicantIface.value = iface;
353                                });
354                    } catch (RemoteException e) {
355                        Log.e(TAG, "ISupplicant.getInterface exception: " + e);
356                        handleRemoteException(e, "getInterface");
357                        return null;
358                    }
359                    break;
360                }
361            }
362            return supplicantIface.value;
363        }
364    }
365
366    /**
367     * Create a STA interface for the specified iface name.
368     *
369     * @param ifaceName Name of the interface.
370     * @return true on success, false otherwise.
371     */
372    private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) {
373        synchronized (mLock) {
374            ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
375            ifaceInfo.name = ifaceName;
376            ifaceInfo.type = IfaceType.STA;
377            Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
378            try {
379                getSupplicantMockableV1_1().addInterface(ifaceInfo,
380                        (SupplicantStatus status, ISupplicantIface iface) -> {
381                            if (status.code != SupplicantStatusCode.SUCCESS
382                                    && status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) {
383                                Log.e(TAG, "Failed to create ISupplicantIface " + status.code);
384                                return;
385                            }
386                            supplicantIface.value = iface;
387                        });
388            } catch (RemoteException e) {
389                Log.e(TAG, "ISupplicant.addInterface exception: " + e);
390                handleRemoteException(e, "addInterface");
391                return null;
392            }
393            return supplicantIface.value;
394        }
395    }
396
397    /**
398     * Teardown a STA interface for the specified iface name.
399     *
400     * @param ifaceName Name of the interface.
401     * @return true on success, false otherwise.
402     */
403    public boolean teardownIface(@NonNull String ifaceName) {
404        synchronized (mLock) {
405            final String methodStr = "teardownIface";
406            if (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) == null) return false;
407            if (isV1_1()) {
408                if (!removeIfaceV1_1(ifaceName)) {
409                    Log.e(TAG, "Failed to remove iface = " + ifaceName);
410                    return false;
411                }
412            }
413            if (mISupplicantStaIfaces.remove(ifaceName) == null) {
414                Log.e(TAG, "Trying to teardown unknown inteface");
415                return false;
416            }
417            mISupplicantStaIfaceCallbacks.remove(ifaceName);
418            return true;
419        }
420    }
421
422    /**
423     * Remove a STA interface for the specified iface name.
424     *
425     * @param ifaceName Name of the interface.
426     * @return true on success, false otherwise.
427     */
428    private boolean removeIfaceV1_1(@NonNull String ifaceName) {
429        synchronized (mLock) {
430            try {
431                ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
432                ifaceInfo.name = ifaceName;
433                ifaceInfo.type = IfaceType.STA;
434                SupplicantStatus status = getSupplicantMockableV1_1().removeInterface(ifaceInfo);
435                if (status.code != SupplicantStatusCode.SUCCESS) {
436                    Log.e(TAG, "Failed to remove iface " + status.code);
437                    return false;
438                }
439            } catch (RemoteException e) {
440                Log.e(TAG, "ISupplicant.removeInterface exception: " + e);
441                handleRemoteException(e, "removeInterface");
442                return false;
443            }
444            return true;
445        }
446    }
447
448    /**
449     * Registers a death notification for supplicant.
450     * @return Returns true on success.
451     */
452    public boolean registerDeathHandler(@NonNull SupplicantDeathEventHandler handler) {
453        if (mDeathEventHandler != null) {
454            Log.e(TAG, "Death handler already present");
455        }
456        mDeathEventHandler = handler;
457        return true;
458    }
459
460    /**
461     * Deregisters a death notification for supplicant.
462     * @return Returns true on success.
463     */
464    public boolean deregisterDeathHandler() {
465        if (mDeathEventHandler == null) {
466            Log.e(TAG, "No Death handler present");
467        }
468        mDeathEventHandler = null;
469        return true;
470    }
471
472
473    private void clearState() {
474        synchronized (mLock) {
475            mISupplicant = null;
476            mISupplicantStaIfaces.clear();
477            mCurrentNetworkLocalConfigs.clear();
478            mCurrentNetworkRemoteHandles.clear();
479        }
480    }
481
482    private void supplicantServiceDiedHandler() {
483        synchronized (mLock) {
484            for (String ifaceName : mISupplicantStaIfaces.keySet()) {
485                mWifiMonitor.broadcastSupplicantDisconnectionEvent(ifaceName);
486            }
487            clearState();
488            if (mDeathEventHandler != null) {
489                mDeathEventHandler.onDeath();
490            }
491        }
492    }
493
494    /**
495     * Signals whether Initialization completed successfully.
496     */
497    public boolean isInitializationStarted() {
498        synchronized (mLock) {
499            return mIServiceManager != null;
500        }
501    }
502
503    /**
504     * Signals whether Initialization completed successfully.
505     */
506    public boolean isInitializationComplete() {
507        synchronized (mLock) {
508            return mISupplicant != null;
509        }
510    }
511
512    /**
513     * Terminate the supplicant daemon.
514     */
515    public void terminate() {
516        synchronized (mLock) {
517            final String methodStr = "terminate";
518            if (!checkSupplicantAndLogFailure(methodStr)) return;
519            try {
520                if (isV1_1()) {
521                    getSupplicantMockableV1_1().terminate();
522                }
523            } catch (RemoteException e) {
524                handleRemoteException(e, methodStr);
525            }
526        }
527    }
528
529    /**
530     * Wrapper functions to access static HAL methods, created to be mockable in unit tests
531     */
532    protected IServiceManager getServiceManagerMockable() throws RemoteException {
533        synchronized (mLock) {
534            return IServiceManager.getService();
535        }
536    }
537
538    protected ISupplicant getSupplicantMockable() throws RemoteException {
539        synchronized (mLock) {
540            try {
541                return ISupplicant.getService();
542            } catch (NoSuchElementException e) {
543                Log.e(TAG, "Failed to get ISupplicant", e);
544                return null;
545            }
546        }
547    }
548
549    protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1()
550            throws RemoteException {
551        synchronized (mLock) {
552            try {
553                return android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom(
554                        ISupplicant.getService());
555            } catch (NoSuchElementException e) {
556                Log.e(TAG, "Failed to get ISupplicant", e);
557                return null;
558            }
559        }
560    }
561
562    protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
563        synchronized (mLock) {
564            return ISupplicantStaIface.asInterface(iface.asBinder());
565        }
566    }
567
568    protected android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface
569            getStaIfaceMockableV1_1(ISupplicantIface iface) {
570        synchronized (mLock) {
571            return android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface.
572                    asInterface(iface.asBinder());
573        }
574    }
575
576    /**
577     * Check if the device is running V1_1 supplicant service.
578     * @return
579     */
580    private boolean isV1_1() {
581        synchronized (mLock) {
582            try {
583                return (getSupplicantMockableV1_1() != null);
584            } catch (RemoteException e) {
585                Log.e(TAG, "ISupplicant.getService exception: " + e);
586                handleRemoteException(e, "getSupplicantMockable");
587                return false;
588            }
589        }
590    }
591
592    /**
593     * Helper method to look up the network object for the specified iface.
594     */
595    private ISupplicantStaIface getStaIface(@NonNull String ifaceName) {
596        return mISupplicantStaIfaces.get(ifaceName);
597    }
598
599    /**
600     * Helper method to look up the network object for the specified iface.
601     */
602    private SupplicantStaNetworkHal getCurrentNetworkRemoteHandle(@NonNull String ifaceName) {
603        return mCurrentNetworkRemoteHandles.get(ifaceName);
604    }
605
606    /**
607     * Helper method to look up the network config or the specified iface.
608     */
609    private WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) {
610        return mCurrentNetworkLocalConfigs.get(ifaceName);
611    }
612
613    /**
614     * Add a network configuration to wpa_supplicant.
615     *
616     * @param config Config corresponding to the network.
617     * @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
618     * for the current network.
619     */
620    private Pair<SupplicantStaNetworkHal, WifiConfiguration>
621            addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
622        synchronized (mLock) {
623            logi("addSupplicantStaNetwork via HIDL");
624            if (config == null) {
625                loge("Cannot add NULL network!");
626                return null;
627            }
628            SupplicantStaNetworkHal network = addNetwork(ifaceName);
629            if (network == null) {
630                loge("Failed to add a network!");
631                return null;
632            }
633            boolean saveSuccess = false;
634            try {
635                saveSuccess = network.saveWifiConfiguration(config);
636            } catch (IllegalArgumentException e) {
637                Log.e(TAG, "Exception while saving config params: " + config, e);
638            }
639            if (!saveSuccess) {
640                loge("Failed to save variables for: " + config.configKey());
641                if (!removeAllNetworks(ifaceName)) {
642                    loge("Failed to remove all networks on failure.");
643                }
644                return null;
645            }
646            return new Pair(network, new WifiConfiguration(config));
647        }
648    }
649
650    /**
651     * Add the provided network configuration to wpa_supplicant and initiate connection to it.
652     * This method does the following:
653     * 1. If |config| is different to the current supplicant network, removes all supplicant
654     * networks and saves |config|.
655     * 2. Select the new network in wpa_supplicant.
656     *
657     * @param ifaceName Name of the interface.
658     * @param config WifiConfiguration parameters for the provided network.
659     * @return {@code true} if it succeeds, {@code false} otherwise
660     */
661    public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
662        synchronized (mLock) {
663            logd("connectToNetwork " + config.configKey());
664            WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
665            if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
666                String networkSelectionBSSID = config.getNetworkSelectionStatus()
667                        .getNetworkSelectionBSSID();
668                String networkSelectionBSSIDCurrent =
669                        currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
670                if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
671                    logd("Network is already saved, will not trigger remove and add operation.");
672                } else {
673                    logd("Network is already saved, but need to update BSSID.");
674                    if (!setCurrentNetworkBssid(
675                            ifaceName,
676                            config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
677                        loge("Failed to set current network BSSID.");
678                        return false;
679                    }
680                    mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
681                }
682            } else {
683                mCurrentNetworkRemoteHandles.remove(ifaceName);
684                mCurrentNetworkLocalConfigs.remove(ifaceName);
685                if (!removeAllNetworks(ifaceName)) {
686                    loge("Failed to remove existing networks");
687                    return false;
688                }
689                Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
690                        addNetworkAndSaveConfig(ifaceName, config);
691                if (pair == null) {
692                    loge("Failed to add/save network configuration: " + config.configKey());
693                    return false;
694                }
695                mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
696                mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
697            }
698            SupplicantStaNetworkHal networkHandle =
699                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
700            if (networkHandle == null || !networkHandle.select()) {
701                loge("Failed to select network configuration: " + config.configKey());
702                return false;
703            }
704            return true;
705        }
706    }
707
708    /**
709     * Initiates roaming to the already configured network in wpa_supplicant. If the network
710     * configuration provided does not match the already configured network, then this triggers
711     * a new connection attempt (instead of roam).
712     * 1. First check if we're attempting to connect to the same network as we currently have
713     * configured.
714     * 2. Set the new bssid for the network in wpa_supplicant.
715     * 3. Trigger reassociate command to wpa_supplicant.
716     *
717     * @param ifaceName Name of the interface.
718     * @param config WifiConfiguration parameters for the provided network.
719     * @return {@code true} if it succeeds, {@code false} otherwise
720     */
721    public boolean roamToNetwork(@NonNull String ifaceName, WifiConfiguration config) {
722        synchronized (mLock) {
723            if (getCurrentNetworkId(ifaceName) != config.networkId) {
724                Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
725                        + "Current network ID: " + getCurrentNetworkId(ifaceName));
726                return connectToNetwork(ifaceName, config);
727            }
728            String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
729            logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
730
731            SupplicantStaNetworkHal networkHandle =
732                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "roamToNetwork");
733            if (networkHandle == null || !networkHandle.setBssid(bssid)) {
734                loge("Failed to set new bssid on network: " + config.configKey());
735                return false;
736            }
737            if (!reassociate(ifaceName)) {
738                loge("Failed to trigger reassociate");
739                return false;
740            }
741            return true;
742        }
743    }
744
745    /**
746     * Load all the configured networks from wpa_supplicant.
747     *
748     * @param ifaceName     Name of the interface.
749     * @param configs       Map of configuration key to configuration objects corresponding to all
750     *                      the networks.
751     * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
752     * @return true if succeeds, false otherwise.
753     */
754    public boolean loadNetworks(@NonNull String ifaceName, Map<String, WifiConfiguration> configs,
755                                SparseArray<Map<String, String>> networkExtras) {
756        synchronized (mLock) {
757            List<Integer> networkIds = listNetworks(ifaceName);
758            if (networkIds == null) {
759                Log.e(TAG, "Failed to list networks");
760                return false;
761            }
762            for (Integer networkId : networkIds) {
763                SupplicantStaNetworkHal network = getNetwork(ifaceName, networkId);
764                if (network == null) {
765                    Log.e(TAG, "Failed to get network with ID: " + networkId);
766                    return false;
767                }
768                WifiConfiguration config = new WifiConfiguration();
769                Map<String, String> networkExtra = new HashMap<>();
770                boolean loadSuccess = false;
771                try {
772                    loadSuccess = network.loadWifiConfiguration(config, networkExtra);
773                } catch (IllegalArgumentException e) {
774                    Log.wtf(TAG, "Exception while loading config params: " + config, e);
775                }
776                if (!loadSuccess) {
777                    Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId
778                            + ". Skipping...");
779                    continue;
780                }
781                // Set the default IP assignments.
782                config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
783                config.setProxySettings(IpConfiguration.ProxySettings.NONE);
784
785                networkExtras.put(networkId, networkExtra);
786                String configKey =
787                        networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
788                final WifiConfiguration duplicateConfig = configs.put(configKey, config);
789                if (duplicateConfig != null) {
790                    // The network is already known. Overwrite the duplicate entry.
791                    Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
792                    removeNetwork(ifaceName, duplicateConfig.networkId);
793                    networkExtras.remove(duplicateConfig.networkId);
794                }
795            }
796            return true;
797        }
798    }
799
800    /**
801     * Remove the request |networkId| from supplicant if it's the current network,
802     * if the current configured network matches |networkId|.
803     *
804     * @param ifaceName Name of the interface.
805     * @param networkId network id of the network to be removed from supplicant.
806     */
807    public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
808        synchronized (mLock) {
809            if (getCurrentNetworkId(ifaceName) == networkId) {
810                // Currently we only save 1 network in supplicant.
811                removeAllNetworks(ifaceName);
812            }
813        }
814    }
815
816    /**
817     * Remove all networks from supplicant
818     *
819     * @param ifaceName Name of the interface.
820     */
821    public boolean removeAllNetworks(@NonNull String ifaceName) {
822        synchronized (mLock) {
823            ArrayList<Integer> networks = listNetworks(ifaceName);
824            if (networks == null) {
825                Log.e(TAG, "removeAllNetworks failed, got null networks");
826                return false;
827            }
828            for (int id : networks) {
829                if (!removeNetwork(ifaceName, id)) {
830                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
831                    return false;
832                }
833            }
834            // Reset current network info.  Probably not needed once we add support to remove/reset
835            // current network on receiving disconnection event from supplicant (b/32898136).
836            mCurrentNetworkRemoteHandles.remove(ifaceName);
837            mCurrentNetworkLocalConfigs.remove(ifaceName);
838            return true;
839        }
840    }
841
842    /**
843     * Set the currently configured network's bssid.
844     *
845     * @param ifaceName Name of the interface.
846     * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
847     * @return true if succeeds, false otherwise.
848     */
849    public boolean setCurrentNetworkBssid(@NonNull String ifaceName, String bssidStr) {
850        synchronized (mLock) {
851            SupplicantStaNetworkHal networkHandle =
852                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "setCurrentNetworkBssid");
853            if (networkHandle == null) return false;
854            return networkHandle.setBssid(bssidStr);
855        }
856    }
857
858    /**
859     * Get the currently configured network's WPS NFC token.
860     *
861     * @param ifaceName Name of the interface.
862     * @return Hex string corresponding to the WPS NFC token.
863     */
864    public String getCurrentNetworkWpsNfcConfigurationToken(@NonNull String ifaceName) {
865        synchronized (mLock) {
866            SupplicantStaNetworkHal networkHandle =
867                    checkSupplicantStaNetworkAndLogFailure(
868                            ifaceName, "getCurrentNetworkWpsNfcConfigurationToken");
869            if (networkHandle == null) return null;
870            return networkHandle.getWpsNfcConfigurationToken();
871        }
872    }
873
874    /**
875     * Get the eap anonymous identity for the currently configured network.
876     *
877     * @param ifaceName Name of the interface.
878     * @return anonymous identity string if succeeds, null otherwise.
879     */
880    public String getCurrentNetworkEapAnonymousIdentity(@NonNull String ifaceName) {
881        synchronized (mLock) {
882            SupplicantStaNetworkHal networkHandle =
883                    checkSupplicantStaNetworkAndLogFailure(
884                            ifaceName, "getCurrentNetworkEapAnonymousIdentity");
885            if (networkHandle == null) return null;
886            return networkHandle.fetchEapAnonymousIdentity();
887        }
888    }
889
890    /**
891     * Send the eap identity response for the currently configured network.
892     *
893     * @param ifaceName Name of the interface.
894     * @param identity identity used for EAP-Identity
895     * @param encryptedIdentity encrypted identity used for EAP-AKA/EAP-SIM
896     * @return true if succeeds, false otherwise.
897     */
898    public boolean sendCurrentNetworkEapIdentityResponse(
899            @NonNull String ifaceName, @NonNull String identity, String encryptedIdentity) {
900        synchronized (mLock) {
901            SupplicantStaNetworkHal networkHandle =
902                    checkSupplicantStaNetworkAndLogFailure(
903                            ifaceName, "sendCurrentNetworkEapIdentityResponse");
904            if (networkHandle == null) return false;
905            return networkHandle.sendNetworkEapIdentityResponse(identity, encryptedIdentity);
906        }
907    }
908
909    /**
910     * Send the eap sim gsm auth response for the currently configured network.
911     *
912     * @param ifaceName Name of the interface.
913     * @param paramsStr String to send.
914     * @return true if succeeds, false otherwise.
915     */
916    public boolean sendCurrentNetworkEapSimGsmAuthResponse(
917            @NonNull String ifaceName, String paramsStr) {
918        synchronized (mLock) {
919            SupplicantStaNetworkHal networkHandle =
920                    checkSupplicantStaNetworkAndLogFailure(
921                            ifaceName, "sendCurrentNetworkEapSimGsmAuthResponse");
922            if (networkHandle == null) return false;
923            return networkHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
924        }
925    }
926
927    /**
928     * Send the eap sim gsm auth failure for the currently configured network.
929     *
930     * @param ifaceName Name of the interface.
931     * @return true if succeeds, false otherwise.
932     */
933    public boolean sendCurrentNetworkEapSimGsmAuthFailure(@NonNull String ifaceName) {
934        synchronized (mLock) {
935            SupplicantStaNetworkHal networkHandle =
936                    checkSupplicantStaNetworkAndLogFailure(
937                            ifaceName, "sendCurrentNetworkEapSimGsmAuthFailure");
938            if (networkHandle == null) return false;
939            return networkHandle.sendNetworkEapSimGsmAuthFailure();
940        }
941    }
942
943    /**
944     * Send the eap sim umts auth response for the currently configured network.
945     *
946     * @param ifaceName Name of the interface.
947     * @param paramsStr String to send.
948     * @return true if succeeds, false otherwise.
949     */
950    public boolean sendCurrentNetworkEapSimUmtsAuthResponse(
951            @NonNull String ifaceName, String paramsStr) {
952        synchronized (mLock) {
953            SupplicantStaNetworkHal networkHandle =
954                    checkSupplicantStaNetworkAndLogFailure(
955                            ifaceName, "sendCurrentNetworkEapSimUmtsAuthResponse");
956            if (networkHandle == null) return false;
957            return networkHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
958        }
959    }
960
961    /**
962     * Send the eap sim umts auts response for the currently configured network.
963     *
964     * @param ifaceName Name of the interface.
965     * @param paramsStr String to send.
966     * @return true if succeeds, false otherwise.
967     */
968    public boolean sendCurrentNetworkEapSimUmtsAutsResponse(
969            @NonNull String ifaceName, String paramsStr) {
970        synchronized (mLock) {
971            SupplicantStaNetworkHal networkHandle =
972                    checkSupplicantStaNetworkAndLogFailure(
973                            ifaceName, "sendCurrentNetworkEapSimUmtsAutsResponse");
974            if (networkHandle == null) return false;
975            return networkHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
976        }
977    }
978
979    /**
980     * Send the eap sim umts auth failure for the currently configured network.
981     *
982     * @param ifaceName Name of the interface.
983     * @return true if succeeds, false otherwise.
984     */
985    public boolean sendCurrentNetworkEapSimUmtsAuthFailure(@NonNull String ifaceName) {
986        synchronized (mLock) {
987            SupplicantStaNetworkHal networkHandle =
988                    checkSupplicantStaNetworkAndLogFailure(
989                            ifaceName, "sendCurrentNetworkEapSimUmtsAuthFailure");
990            if (networkHandle == null) return false;
991            return networkHandle.sendNetworkEapSimUmtsAuthFailure();
992        }
993    }
994
995    /**
996     * Adds a new network.
997     *
998     * @return The ISupplicantNetwork object for the new network, or null if the call fails
999     */
1000    private SupplicantStaNetworkHal addNetwork(@NonNull String ifaceName) {
1001        synchronized (mLock) {
1002            final String methodStr = "addNetwork";
1003            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1004            if (iface == null) return null;
1005            Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
1006            try {
1007                iface.addNetwork((SupplicantStatus status,
1008                        ISupplicantNetwork network) -> {
1009                    if (checkStatusAndLogFailure(status, methodStr)) {
1010                        newNetwork.value = network;
1011                    }
1012                });
1013            } catch (RemoteException e) {
1014                handleRemoteException(e, methodStr);
1015            }
1016            if (newNetwork.value != null) {
1017                return getStaNetworkMockable(
1018                        ifaceName,
1019                        ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
1020            } else {
1021                return null;
1022            }
1023        }
1024    }
1025
1026    /**
1027     * Remove network from supplicant with network Id
1028     *
1029     * @return true if request is sent successfully, false otherwise.
1030     */
1031    private boolean removeNetwork(@NonNull String ifaceName, int id) {
1032        synchronized (mLock) {
1033            final String methodStr = "removeNetwork";
1034            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1035            if (iface == null) return false;
1036            try {
1037                SupplicantStatus status = iface.removeNetwork(id);
1038                return checkStatusAndLogFailure(status, methodStr);
1039            } catch (RemoteException e) {
1040                handleRemoteException(e, methodStr);
1041                return false;
1042            }
1043        }
1044    }
1045
1046    /**
1047     * Use this to mock the creation of SupplicantStaNetworkHal instance.
1048     *
1049     * @param ifaceName Name of the interface.
1050     * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
1051     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
1052     * the call fails
1053     */
1054    protected SupplicantStaNetworkHal getStaNetworkMockable(
1055            @NonNull String ifaceName, ISupplicantStaNetwork iSupplicantStaNetwork) {
1056        synchronized (mLock) {
1057            SupplicantStaNetworkHal network =
1058                    new SupplicantStaNetworkHal(iSupplicantStaNetwork, ifaceName, mContext,
1059                            mWifiMonitor);
1060            if (network != null) {
1061                network.enableVerboseLogging(mVerboseLoggingEnabled);
1062            }
1063            return network;
1064        }
1065    }
1066
1067    /**
1068     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
1069     * the call fails
1070     */
1071    private SupplicantStaNetworkHal getNetwork(@NonNull String ifaceName, int id) {
1072        synchronized (mLock) {
1073            final String methodStr = "getNetwork";
1074            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1075            if (iface == null) return null;
1076            Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
1077            try {
1078                iface.getNetwork(id, (SupplicantStatus status, ISupplicantNetwork network) -> {
1079                    if (checkStatusAndLogFailure(status, methodStr)) {
1080                        gotNetwork.value = network;
1081                    }
1082                });
1083            } catch (RemoteException e) {
1084                handleRemoteException(e, methodStr);
1085            }
1086            if (gotNetwork.value != null) {
1087                return getStaNetworkMockable(
1088                        ifaceName,
1089                        ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
1090            } else {
1091                return null;
1092            }
1093        }
1094    }
1095
1096    /** See ISupplicantStaNetwork.hal for documentation */
1097    private boolean registerCallback(
1098            ISupplicantStaIface iface, ISupplicantStaIfaceCallback callback) {
1099        synchronized (mLock) {
1100            final String methodStr = "registerCallback";
1101            if (iface == null) return false;
1102            try {
1103                SupplicantStatus status =  iface.registerCallback(callback);
1104                return checkStatusAndLogFailure(status, methodStr);
1105            } catch (RemoteException e) {
1106                handleRemoteException(e, methodStr);
1107                return false;
1108            }
1109        }
1110    }
1111
1112    private boolean registerCallbackV1_1(
1113            android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface iface,
1114            android.hardware.wifi.supplicant.V1_1.ISupplicantStaIfaceCallback callback) {
1115        synchronized (mLock) {
1116            String methodStr = "registerCallback_1_1";
1117
1118            if (iface == null) return false;
1119            try {
1120                SupplicantStatus status =  iface.registerCallback_1_1(callback);
1121                return checkStatusAndLogFailure(status, methodStr);
1122            } catch (RemoteException e) {
1123                handleRemoteException(e, methodStr);
1124                return false;
1125            }
1126        }
1127    }
1128
1129    /**
1130     * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
1131     * null if the call fails
1132     */
1133    private java.util.ArrayList<Integer> listNetworks(@NonNull String ifaceName) {
1134        synchronized (mLock) {
1135            final String methodStr = "listNetworks";
1136            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1137            if (iface == null) return null;
1138            Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
1139            try {
1140                iface.listNetworks((SupplicantStatus status, ArrayList<Integer> networkIds) -> {
1141                    if (checkStatusAndLogFailure(status, methodStr)) {
1142                        networkIdList.value = networkIds;
1143                    }
1144                });
1145            } catch (RemoteException e) {
1146                handleRemoteException(e, methodStr);
1147            }
1148            return networkIdList.value;
1149        }
1150    }
1151
1152    /**
1153     * Set WPS device name.
1154     *
1155     * @param ifaceName Name of the interface.
1156     * @param name String to be set.
1157     * @return true if request is sent successfully, false otherwise.
1158     */
1159    public boolean setWpsDeviceName(@NonNull String ifaceName, String name) {
1160        synchronized (mLock) {
1161            final String methodStr = "setWpsDeviceName";
1162            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1163            if (iface == null) return false;
1164            try {
1165                SupplicantStatus status = iface.setWpsDeviceName(name);
1166                return checkStatusAndLogFailure(status, methodStr);
1167            } catch (RemoteException e) {
1168                handleRemoteException(e, methodStr);
1169                return false;
1170            }
1171        }
1172    }
1173
1174    /**
1175     * Set WPS device type.
1176     *
1177     * @param ifaceName Name of the interface.
1178     * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
1179     * @return true if request is sent successfully, false otherwise.
1180     */
1181    public boolean setWpsDeviceType(@NonNull String ifaceName, String typeStr) {
1182        synchronized (mLock) {
1183            try {
1184                Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
1185                if (!match.find() || match.groupCount() != 3) {
1186                    Log.e(TAG, "Malformed WPS device type " + typeStr);
1187                    return false;
1188                }
1189                short categ = Short.parseShort(match.group(1));
1190                byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
1191                short subCateg = Short.parseShort(match.group(3));
1192
1193                byte[] bytes = new byte[8];
1194                ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
1195                byteBuffer.putShort(categ);
1196                byteBuffer.put(oui);
1197                byteBuffer.putShort(subCateg);
1198                return setWpsDeviceType(ifaceName, bytes);
1199            } catch (IllegalArgumentException e) {
1200                Log.e(TAG, "Illegal argument " + typeStr, e);
1201                return false;
1202            }
1203        }
1204    }
1205
1206    private boolean setWpsDeviceType(@NonNull String ifaceName, byte[/* 8 */] type) {
1207        synchronized (mLock) {
1208            final String methodStr = "setWpsDeviceType";
1209            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1210            if (iface == null) return false;
1211            try {
1212                SupplicantStatus status = iface.setWpsDeviceType(type);
1213                return checkStatusAndLogFailure(status, methodStr);
1214            } catch (RemoteException e) {
1215                handleRemoteException(e, methodStr);
1216                return false;
1217            }
1218        }
1219    }
1220
1221    /**
1222     * Set WPS manufacturer.
1223     *
1224     * @param ifaceName Name of the interface.
1225     * @param manufacturer String to be set.
1226     * @return true if request is sent successfully, false otherwise.
1227     */
1228    public boolean setWpsManufacturer(@NonNull String ifaceName, String manufacturer) {
1229        synchronized (mLock) {
1230            final String methodStr = "setWpsManufacturer";
1231            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1232            if (iface == null) return false;
1233            try {
1234                SupplicantStatus status = iface.setWpsManufacturer(manufacturer);
1235                return checkStatusAndLogFailure(status, methodStr);
1236            } catch (RemoteException e) {
1237                handleRemoteException(e, methodStr);
1238                return false;
1239            }
1240        }
1241    }
1242
1243    /**
1244     * Set WPS model name.
1245     *
1246     * @param ifaceName Name of the interface.
1247     * @param modelName String to be set.
1248     * @return true if request is sent successfully, false otherwise.
1249     */
1250    public boolean setWpsModelName(@NonNull String ifaceName, String modelName) {
1251        synchronized (mLock) {
1252            final String methodStr = "setWpsModelName";
1253            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1254            if (iface == null) return false;
1255            try {
1256                SupplicantStatus status = iface.setWpsModelName(modelName);
1257                return checkStatusAndLogFailure(status, methodStr);
1258            } catch (RemoteException e) {
1259                handleRemoteException(e, methodStr);
1260                return false;
1261            }
1262        }
1263    }
1264
1265    /**
1266     * Set WPS model number.
1267     *
1268     * @param ifaceName Name of the interface.
1269     * @param modelNumber String to be set.
1270     * @return true if request is sent successfully, false otherwise.
1271     */
1272    public boolean setWpsModelNumber(@NonNull String ifaceName, String modelNumber) {
1273        synchronized (mLock) {
1274            final String methodStr = "setWpsModelNumber";
1275            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1276            if (iface == null) return false;
1277            try {
1278                SupplicantStatus status = iface.setWpsModelNumber(modelNumber);
1279                return checkStatusAndLogFailure(status, methodStr);
1280            } catch (RemoteException e) {
1281                handleRemoteException(e, methodStr);
1282                return false;
1283            }
1284        }
1285    }
1286
1287    /**
1288     * Set WPS serial number.
1289     *
1290     * @param ifaceName Name of the interface.
1291     * @param serialNumber String to be set.
1292     * @return true if request is sent successfully, false otherwise.
1293     */
1294    public boolean setWpsSerialNumber(@NonNull String ifaceName, String serialNumber) {
1295        synchronized (mLock) {
1296            final String methodStr = "setWpsSerialNumber";
1297            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1298            if (iface == null) return false;
1299            try {
1300                SupplicantStatus status = iface.setWpsSerialNumber(serialNumber);
1301                return checkStatusAndLogFailure(status, methodStr);
1302            } catch (RemoteException e) {
1303                handleRemoteException(e, methodStr);
1304                return false;
1305            }
1306        }
1307    }
1308
1309    /**
1310     * Set WPS config methods
1311     *
1312     * @param ifaceName Name of the interface.
1313     * @param configMethodsStr List of config methods.
1314     * @return true if request is sent successfully, false otherwise.
1315     */
1316    public boolean setWpsConfigMethods(@NonNull String ifaceName, String configMethodsStr) {
1317        synchronized (mLock) {
1318            short configMethodsMask = 0;
1319            String[] configMethodsStrArr = configMethodsStr.split("\\s+");
1320            for (int i = 0; i < configMethodsStrArr.length; i++) {
1321                configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
1322            }
1323            return setWpsConfigMethods(ifaceName, configMethodsMask);
1324        }
1325    }
1326
1327    private boolean setWpsConfigMethods(@NonNull String ifaceName, short configMethods) {
1328        synchronized (mLock) {
1329            final String methodStr = "setWpsConfigMethods";
1330            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1331            if (iface == null) return false;
1332            try {
1333                SupplicantStatus status = iface.setWpsConfigMethods(configMethods);
1334                return checkStatusAndLogFailure(status, methodStr);
1335            } catch (RemoteException e) {
1336                handleRemoteException(e, methodStr);
1337                return false;
1338            }
1339        }
1340    }
1341
1342    /**
1343     * Trigger a reassociation even if the iface is currently connected.
1344     *
1345     * @param ifaceName Name of the interface.
1346     * @return true if request is sent successfully, false otherwise.
1347     */
1348    public boolean reassociate(@NonNull String ifaceName) {
1349        synchronized (mLock) {
1350            final String methodStr = "reassociate";
1351            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1352            if (iface == null) return false;
1353            try {
1354                SupplicantStatus status = iface.reassociate();
1355                return checkStatusAndLogFailure(status, methodStr);
1356            } catch (RemoteException e) {
1357                handleRemoteException(e, methodStr);
1358                return false;
1359            }
1360        }
1361    }
1362
1363    /**
1364     * Trigger a reconnection if the iface is disconnected.
1365     *
1366     * @param ifaceName Name of the interface.
1367     * @return true if request is sent successfully, false otherwise.
1368     */
1369    public boolean reconnect(@NonNull String ifaceName) {
1370        synchronized (mLock) {
1371            final String methodStr = "reconnect";
1372            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1373            if (iface == null) return false;
1374            try {
1375                SupplicantStatus status = iface.reconnect();
1376                return checkStatusAndLogFailure(status, methodStr);
1377            } catch (RemoteException e) {
1378                handleRemoteException(e, methodStr);
1379                return false;
1380            }
1381        }
1382    }
1383
1384    /**
1385     * Trigger a disconnection from the currently connected network.
1386     *
1387     * @param ifaceName Name of the interface.
1388     * @return true if request is sent successfully, false otherwise.
1389     */
1390    public boolean disconnect(@NonNull String ifaceName) {
1391        synchronized (mLock) {
1392            final String methodStr = "disconnect";
1393            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1394            if (iface == null) return false;
1395            try {
1396                SupplicantStatus status = iface.disconnect();
1397                return checkStatusAndLogFailure(status, methodStr);
1398            } catch (RemoteException e) {
1399                handleRemoteException(e, methodStr);
1400                return false;
1401            }
1402        }
1403    }
1404
1405    /**
1406     * Enable or disable power save mode.
1407     *
1408     * @param ifaceName Name of the interface.
1409     * @param enable true to enable, false to disable.
1410     * @return true if request is sent successfully, false otherwise.
1411     */
1412    public boolean setPowerSave(@NonNull String ifaceName, boolean enable) {
1413        synchronized (mLock) {
1414            final String methodStr = "setPowerSave";
1415            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1416            if (iface == null) return false;
1417            try {
1418                SupplicantStatus status = iface.setPowerSave(enable);
1419                return checkStatusAndLogFailure(status, methodStr);
1420            } catch (RemoteException e) {
1421                handleRemoteException(e, methodStr);
1422                return false;
1423            }
1424        }
1425    }
1426
1427    /**
1428     * Initiate TDLS discover with the specified AP.
1429     *
1430     * @param ifaceName Name of the interface.
1431     * @param macAddress MAC Address of the AP.
1432     * @return true if request is sent successfully, false otherwise.
1433     */
1434    public boolean initiateTdlsDiscover(@NonNull String ifaceName, String macAddress) {
1435        synchronized (mLock) {
1436            try {
1437                return initiateTdlsDiscover(
1438                        ifaceName, NativeUtil.macAddressToByteArray(macAddress));
1439            } catch (IllegalArgumentException e) {
1440                Log.e(TAG, "Illegal argument " + macAddress, e);
1441                return false;
1442            }
1443        }
1444    }
1445    /** See ISupplicantStaIface.hal for documentation */
1446    private boolean initiateTdlsDiscover(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
1447        synchronized (mLock) {
1448            final String methodStr = "initiateTdlsDiscover";
1449            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1450            if (iface == null) return false;
1451            try {
1452                SupplicantStatus status = iface.initiateTdlsDiscover(macAddress);
1453                return checkStatusAndLogFailure(status, methodStr);
1454            } catch (RemoteException e) {
1455                handleRemoteException(e, methodStr);
1456                return false;
1457            }
1458        }
1459    }
1460
1461    /**
1462     * Initiate TDLS setup with the specified AP.
1463     *
1464     * @param ifaceName Name of the interface.
1465     * @param macAddress MAC Address of the AP.
1466     * @return true if request is sent successfully, false otherwise.
1467     */
1468    public boolean initiateTdlsSetup(@NonNull String ifaceName, String macAddress) {
1469        synchronized (mLock) {
1470            try {
1471                return initiateTdlsSetup(ifaceName, NativeUtil.macAddressToByteArray(macAddress));
1472            } catch (IllegalArgumentException e) {
1473                Log.e(TAG, "Illegal argument " + macAddress, e);
1474                return false;
1475            }
1476        }
1477    }
1478    /** See ISupplicantStaIface.hal for documentation */
1479    private boolean initiateTdlsSetup(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
1480        synchronized (mLock) {
1481            final String methodStr = "initiateTdlsSetup";
1482            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1483            if (iface == null) return false;
1484            try {
1485                SupplicantStatus status = iface.initiateTdlsSetup(macAddress);
1486                return checkStatusAndLogFailure(status, methodStr);
1487            } catch (RemoteException e) {
1488                handleRemoteException(e, methodStr);
1489                return false;
1490            }
1491        }
1492    }
1493
1494    /**
1495     * Initiate TDLS teardown with the specified AP.
1496     * @param ifaceName Name of the interface.
1497     * @param macAddress MAC Address of the AP.
1498     * @return true if request is sent successfully, false otherwise.
1499     */
1500    public boolean initiateTdlsTeardown(@NonNull String ifaceName, String macAddress) {
1501        synchronized (mLock) {
1502            try {
1503                return initiateTdlsTeardown(
1504                        ifaceName, NativeUtil.macAddressToByteArray(macAddress));
1505            } catch (IllegalArgumentException e) {
1506                Log.e(TAG, "Illegal argument " + macAddress, e);
1507                return false;
1508            }
1509        }
1510    }
1511
1512    /** See ISupplicantStaIface.hal for documentation */
1513    private boolean initiateTdlsTeardown(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
1514        synchronized (mLock) {
1515            final String methodStr = "initiateTdlsTeardown";
1516            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1517            if (iface == null) return false;
1518            try {
1519                SupplicantStatus status = iface.initiateTdlsTeardown(macAddress);
1520                return checkStatusAndLogFailure(status, methodStr);
1521            } catch (RemoteException e) {
1522                handleRemoteException(e, methodStr);
1523                return false;
1524            }
1525        }
1526    }
1527
1528    /**
1529     * Request the specified ANQP elements |elements| from the specified AP |bssid|.
1530     *
1531     * @param ifaceName Name of the interface.
1532     * @param bssid BSSID of the AP
1533     * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
1534     * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
1535     * @return true if request is sent successfully, false otherwise.
1536     */
1537    public boolean initiateAnqpQuery(@NonNull String ifaceName, String bssid,
1538                                     ArrayList<Short> infoElements,
1539                                     ArrayList<Integer> hs20SubTypes) {
1540        synchronized (mLock) {
1541            try {
1542                return initiateAnqpQuery(
1543                        ifaceName,
1544                        NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
1545            } catch (IllegalArgumentException e) {
1546                Log.e(TAG, "Illegal argument " + bssid, e);
1547                return false;
1548            }
1549        }
1550    }
1551
1552    /** See ISupplicantStaIface.hal for documentation */
1553    private boolean initiateAnqpQuery(@NonNull String ifaceName, byte[/* 6 */] macAddress,
1554            java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
1555        synchronized (mLock) {
1556            final String methodStr = "initiateAnqpQuery";
1557            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1558            if (iface == null) return false;
1559            try {
1560                SupplicantStatus status = iface.initiateAnqpQuery(
1561                        macAddress, infoElements, subTypes);
1562                return checkStatusAndLogFailure(status, methodStr);
1563            } catch (RemoteException e) {
1564                handleRemoteException(e, methodStr);
1565                return false;
1566            }
1567        }
1568    }
1569
1570    /**
1571     * Request the specified ANQP ICON from the specified AP |bssid|.
1572     *
1573     * @param ifaceName Name of the interface.
1574     * @param bssid BSSID of the AP
1575     * @param fileName Name of the file to request.
1576     * @return true if request is sent successfully, false otherwise.
1577     */
1578    public boolean initiateHs20IconQuery(@NonNull String ifaceName, String bssid, String fileName) {
1579        synchronized (mLock) {
1580            try {
1581                return initiateHs20IconQuery(
1582                        ifaceName, NativeUtil.macAddressToByteArray(bssid), fileName);
1583            } catch (IllegalArgumentException e) {
1584                Log.e(TAG, "Illegal argument " + bssid, e);
1585                return false;
1586            }
1587        }
1588    }
1589
1590    /** See ISupplicantStaIface.hal for documentation */
1591    private boolean initiateHs20IconQuery(@NonNull String ifaceName,
1592                                          byte[/* 6 */] macAddress, String fileName) {
1593        synchronized (mLock) {
1594            final String methodStr = "initiateHs20IconQuery";
1595            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1596            if (iface == null) return false;
1597            try {
1598                SupplicantStatus status = iface.initiateHs20IconQuery(macAddress, fileName);
1599                return checkStatusAndLogFailure(status, methodStr);
1600            } catch (RemoteException e) {
1601                handleRemoteException(e, methodStr);
1602                return false;
1603            }
1604        }
1605    }
1606
1607    /**
1608     * Makes a callback to HIDL to getMacAddress from supplicant
1609     *
1610     * @param ifaceName Name of the interface.
1611     * @return string containing the MAC address, or null on a failed call
1612     */
1613    public String getMacAddress(@NonNull String ifaceName) {
1614        synchronized (mLock) {
1615            final String methodStr = "getMacAddress";
1616            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1617            if (iface == null) return null;
1618            Mutable<String> gotMac = new Mutable<>();
1619            try {
1620                iface.getMacAddress((SupplicantStatus status,
1621                        byte[/* 6 */] macAddr) -> {
1622                    if (checkStatusAndLogFailure(status, methodStr)) {
1623                        gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
1624                    }
1625                });
1626            } catch (RemoteException e) {
1627                handleRemoteException(e, methodStr);
1628            }
1629            return gotMac.value;
1630        }
1631    }
1632
1633    /**
1634     * Start using the added RX filters.
1635     *
1636     * @param ifaceName Name of the interface.
1637     * @return true if request is sent successfully, false otherwise.
1638     */
1639    public boolean startRxFilter(@NonNull String ifaceName) {
1640        synchronized (mLock) {
1641            final String methodStr = "startRxFilter";
1642            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1643            if (iface == null) return false;
1644            try {
1645                SupplicantStatus status = iface.startRxFilter();
1646                return checkStatusAndLogFailure(status, methodStr);
1647            } catch (RemoteException e) {
1648                handleRemoteException(e, methodStr);
1649                return false;
1650            }
1651        }
1652    }
1653
1654    /**
1655     * Stop using the added RX filters.
1656     *
1657     * @param ifaceName Name of the interface.
1658     * @return true if request is sent successfully, false otherwise.
1659     */
1660    public boolean stopRxFilter(@NonNull String ifaceName) {
1661        synchronized (mLock) {
1662            final String methodStr = "stopRxFilter";
1663            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1664            if (iface == null) return false;
1665            try {
1666                SupplicantStatus status = iface.stopRxFilter();
1667                return checkStatusAndLogFailure(status, methodStr);
1668            } catch (RemoteException e) {
1669                handleRemoteException(e, methodStr);
1670                return false;
1671            }
1672        }
1673    }
1674
1675    /**
1676     * Add an RX filter.
1677     *
1678     * @param ifaceName Name of the interface.
1679     * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
1680     *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
1681     * @return true if request is sent successfully, false otherwise.
1682     */
1683    public boolean addRxFilter(@NonNull String ifaceName, int type) {
1684        synchronized (mLock) {
1685            byte halType;
1686            switch (type) {
1687                case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
1688                    halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
1689                    break;
1690                case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
1691                    halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1692                    break;
1693                default:
1694                    Log.e(TAG, "Invalid Rx Filter type: " + type);
1695                    return false;
1696            }
1697            return addRxFilter(ifaceName, halType);
1698        }
1699    }
1700
1701    private boolean addRxFilter(@NonNull String ifaceName, byte type) {
1702        synchronized (mLock) {
1703            final String methodStr = "addRxFilter";
1704            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1705            if (iface == null) return false;
1706            try {
1707                SupplicantStatus status = iface.addRxFilter(type);
1708                return checkStatusAndLogFailure(status, methodStr);
1709            } catch (RemoteException e) {
1710                handleRemoteException(e, methodStr);
1711                return false;
1712            }
1713        }
1714    }
1715
1716    /**
1717     * Remove an RX filter.
1718     *
1719     * @param ifaceName Name of the interface.
1720     * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
1721     *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
1722     * @return true if request is sent successfully, false otherwise.
1723     */
1724    public boolean removeRxFilter(@NonNull String ifaceName, int type) {
1725        synchronized (mLock) {
1726            byte halType;
1727            switch (type) {
1728                case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
1729                    halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
1730                    break;
1731                case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
1732                    halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1733                    break;
1734                default:
1735                    Log.e(TAG, "Invalid Rx Filter type: " + type);
1736                    return false;
1737            }
1738            return removeRxFilter(ifaceName, halType);
1739        }
1740    }
1741
1742    private boolean removeRxFilter(@NonNull String ifaceName, byte type) {
1743        synchronized (mLock) {
1744            final String methodStr = "removeRxFilter";
1745            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1746            if (iface == null) return false;
1747            try {
1748                SupplicantStatus status = iface.removeRxFilter(type);
1749                return checkStatusAndLogFailure(status, methodStr);
1750            } catch (RemoteException e) {
1751                handleRemoteException(e, methodStr);
1752                return false;
1753            }
1754        }
1755    }
1756
1757    /**
1758     * Set Bt co existense mode.
1759     *
1760     * @param ifaceName Name of the interface.
1761     * @param mode one of the above {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_DISABLED},
1762     *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_ENABLED} or
1763     *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_SENSE}.
1764     * @return true if request is sent successfully, false otherwise.
1765     */
1766    public boolean setBtCoexistenceMode(@NonNull String ifaceName, int mode) {
1767        synchronized (mLock) {
1768            byte halMode;
1769            switch (mode) {
1770                case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
1771                    halMode = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
1772                    break;
1773                case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
1774                    halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
1775                    break;
1776                case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
1777                    halMode = ISupplicantStaIface.BtCoexistenceMode.SENSE;
1778                    break;
1779                default:
1780                    Log.e(TAG, "Invalid Bt Coex mode: " + mode);
1781                    return false;
1782            }
1783            return setBtCoexistenceMode(ifaceName, halMode);
1784        }
1785    }
1786
1787    private boolean setBtCoexistenceMode(@NonNull String ifaceName, byte mode) {
1788        synchronized (mLock) {
1789            final String methodStr = "setBtCoexistenceMode";
1790            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1791            if (iface == null) return false;
1792            try {
1793                SupplicantStatus status = iface.setBtCoexistenceMode(mode);
1794                return checkStatusAndLogFailure(status, methodStr);
1795            } catch (RemoteException e) {
1796                handleRemoteException(e, methodStr);
1797                return false;
1798            }
1799        }
1800    }
1801
1802    /** Enable or disable BT coexistence mode.
1803     *
1804     * @param ifaceName Name of the interface.
1805     * @param enable true to enable, false to disable.
1806     * @return true if request is sent successfully, false otherwise.
1807     */
1808    public boolean setBtCoexistenceScanModeEnabled(@NonNull String ifaceName, boolean enable) {
1809        synchronized (mLock) {
1810            final String methodStr = "setBtCoexistenceScanModeEnabled";
1811            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1812            if (iface == null) return false;
1813            try {
1814                SupplicantStatus status =
1815                        iface.setBtCoexistenceScanModeEnabled(enable);
1816                return checkStatusAndLogFailure(status, methodStr);
1817            } catch (RemoteException e) {
1818                handleRemoteException(e, methodStr);
1819                return false;
1820            }
1821        }
1822    }
1823
1824    /**
1825     * Enable or disable suspend mode optimizations.
1826     *
1827     * @param ifaceName Name of the interface.
1828     * @param enable true to enable, false otherwise.
1829     * @return true if request is sent successfully, false otherwise.
1830     */
1831    public boolean setSuspendModeEnabled(@NonNull String ifaceName, boolean enable) {
1832        synchronized (mLock) {
1833            final String methodStr = "setSuspendModeEnabled";
1834            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1835            if (iface == null) return false;
1836            try {
1837                SupplicantStatus status = iface.setSuspendModeEnabled(enable);
1838                return checkStatusAndLogFailure(status, methodStr);
1839            } catch (RemoteException e) {
1840                handleRemoteException(e, methodStr);
1841                return false;
1842            }
1843        }
1844    }
1845
1846    /**
1847     * Set country code.
1848     *
1849     * @param ifaceName Name of the interface.
1850     * @param codeStr 2 byte ASCII string. For ex: US, CA.
1851     * @return true if request is sent successfully, false otherwise.
1852     */
1853    public boolean setCountryCode(@NonNull String ifaceName, String codeStr) {
1854        synchronized (mLock) {
1855            if (TextUtils.isEmpty(codeStr)) return false;
1856            return setCountryCode(ifaceName, NativeUtil.stringToByteArray(codeStr));
1857        }
1858    }
1859
1860    /** See ISupplicantStaIface.hal for documentation */
1861    private boolean setCountryCode(@NonNull String ifaceName, byte[/* 2 */] code) {
1862        synchronized (mLock) {
1863            final String methodStr = "setCountryCode";
1864            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1865            if (iface == null) return false;
1866            try {
1867                SupplicantStatus status = iface.setCountryCode(code);
1868                return checkStatusAndLogFailure(status, methodStr);
1869            } catch (RemoteException e) {
1870                handleRemoteException(e, methodStr);
1871                return false;
1872            }
1873        }
1874    }
1875
1876    /**
1877     * Start WPS pin registrar operation with the specified peer and pin.
1878     *
1879     * @param ifaceName Name of the interface.
1880     * @param bssidStr BSSID of the peer.
1881     * @param pin Pin to be used.
1882     * @return true if request is sent successfully, false otherwise.
1883     */
1884    public boolean startWpsRegistrar(@NonNull String ifaceName, String bssidStr, String pin) {
1885        synchronized (mLock) {
1886            if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
1887            try {
1888                return startWpsRegistrar(
1889                        ifaceName, NativeUtil.macAddressToByteArray(bssidStr), pin);
1890            } catch (IllegalArgumentException e) {
1891                Log.e(TAG, "Illegal argument " + bssidStr, e);
1892                return false;
1893            }
1894        }
1895    }
1896
1897    /** See ISupplicantStaIface.hal for documentation */
1898    private boolean startWpsRegistrar(@NonNull String ifaceName, byte[/* 6 */] bssid, String pin) {
1899        synchronized (mLock) {
1900            final String methodStr = "startWpsRegistrar";
1901            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1902            if (iface == null) return false;
1903            try {
1904                SupplicantStatus status = iface.startWpsRegistrar(bssid, pin);
1905                return checkStatusAndLogFailure(status, methodStr);
1906            } catch (RemoteException e) {
1907                handleRemoteException(e, methodStr);
1908                return false;
1909            }
1910        }
1911    }
1912
1913    /**
1914     * Start WPS pin display operation with the specified peer.
1915     *
1916     * @param ifaceName Name of the interface.
1917     * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
1918     * @return true if request is sent successfully, false otherwise.
1919     */
1920    public boolean startWpsPbc(@NonNull String ifaceName, String bssidStr) {
1921        synchronized (mLock) {
1922            try {
1923                return startWpsPbc(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
1924            } catch (IllegalArgumentException e) {
1925                Log.e(TAG, "Illegal argument " + bssidStr, e);
1926                return false;
1927            }
1928        }
1929    }
1930
1931    /** See ISupplicantStaIface.hal for documentation */
1932    private boolean startWpsPbc(@NonNull String ifaceName, byte[/* 6 */] bssid) {
1933        synchronized (mLock) {
1934            final String methodStr = "startWpsPbc";
1935            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1936            if (iface == null) return false;
1937            try {
1938                SupplicantStatus status = iface.startWpsPbc(bssid);
1939                return checkStatusAndLogFailure(status, methodStr);
1940            } catch (RemoteException e) {
1941                handleRemoteException(e, methodStr);
1942                return false;
1943            }
1944        }
1945    }
1946
1947    /**
1948     * Start WPS pin keypad operation with the specified pin.
1949     *
1950     * @param ifaceName Name of the interface.
1951     * @param pin Pin to be used.
1952     * @return true if request is sent successfully, false otherwise.
1953     */
1954    public boolean startWpsPinKeypad(@NonNull String ifaceName, String pin) {
1955        if (TextUtils.isEmpty(pin)) return false;
1956        synchronized (mLock) {
1957            final String methodStr = "startWpsPinKeypad";
1958            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1959            if (iface == null) return false;
1960            try {
1961                SupplicantStatus status = iface.startWpsPinKeypad(pin);
1962                return checkStatusAndLogFailure(status, methodStr);
1963            } catch (RemoteException e) {
1964                handleRemoteException(e, methodStr);
1965                return false;
1966            }
1967        }
1968    }
1969
1970    /**
1971     * Start WPS pin display operation with the specified peer.
1972     *
1973     * @param ifaceName Name of the interface.
1974     * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
1975     * @return new pin generated on success, null otherwise.
1976     */
1977    public String startWpsPinDisplay(@NonNull String ifaceName, String bssidStr) {
1978        synchronized (mLock) {
1979            try {
1980                return startWpsPinDisplay(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
1981            } catch (IllegalArgumentException e) {
1982                Log.e(TAG, "Illegal argument " + bssidStr, e);
1983                return null;
1984            }
1985        }
1986    }
1987
1988    /** See ISupplicantStaIface.hal for documentation */
1989    private String startWpsPinDisplay(@NonNull String ifaceName, byte[/* 6 */] bssid) {
1990        synchronized (mLock) {
1991            final String methodStr = "startWpsPinDisplay";
1992            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
1993            if (iface == null) return null;
1994            final Mutable<String> gotPin = new Mutable<>();
1995            try {
1996                iface.startWpsPinDisplay(bssid,
1997                        (SupplicantStatus status, String pin) -> {
1998                            if (checkStatusAndLogFailure(status, methodStr)) {
1999                                gotPin.value = pin;
2000                            }
2001                        });
2002            } catch (RemoteException e) {
2003                handleRemoteException(e, methodStr);
2004            }
2005            return gotPin.value;
2006        }
2007    }
2008
2009    /**
2010     * Cancels any ongoing WPS requests.
2011     *
2012     * @param ifaceName Name of the interface.
2013     * @return true if request is sent successfully, false otherwise.
2014     */
2015    public boolean cancelWps(@NonNull String ifaceName) {
2016        synchronized (mLock) {
2017            final String methodStr = "cancelWps";
2018            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
2019            if (iface == null) return false;
2020            try {
2021                SupplicantStatus status = iface.cancelWps();
2022                return checkStatusAndLogFailure(status, methodStr);
2023            } catch (RemoteException e) {
2024                handleRemoteException(e, methodStr);
2025                return false;
2026            }
2027        }
2028    }
2029
2030    /**
2031     * Sets whether to use external sim for SIM/USIM processing.
2032     *
2033     * @param ifaceName Name of the interface.
2034     * @param useExternalSim true to enable, false otherwise.
2035     * @return true if request is sent successfully, false otherwise.
2036     */
2037    public boolean setExternalSim(@NonNull String ifaceName, boolean useExternalSim) {
2038        synchronized (mLock) {
2039            final String methodStr = "setExternalSim";
2040            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
2041            if (iface == null) return false;
2042            try {
2043                SupplicantStatus status = iface.setExternalSim(useExternalSim);
2044                return checkStatusAndLogFailure(status, methodStr);
2045            } catch (RemoteException e) {
2046                handleRemoteException(e, methodStr);
2047                return false;
2048            }
2049        }
2050    }
2051
2052    /** See ISupplicant.hal for documentation */
2053    public boolean enableAutoReconnect(@NonNull String ifaceName, boolean enable) {
2054        synchronized (mLock) {
2055            final String methodStr = "enableAutoReconnect";
2056            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
2057            if (iface == null) return false;
2058            try {
2059                SupplicantStatus status = iface.enableAutoReconnect(enable);
2060                return checkStatusAndLogFailure(status, methodStr);
2061            } catch (RemoteException e) {
2062                handleRemoteException(e, methodStr);
2063                return false;
2064            }
2065        }
2066    }
2067
2068    /**
2069     * Set the debug log level for wpa_supplicant
2070     *
2071     * @param turnOnVerbose Whether to turn on verbose logging or not.
2072     * @return true if request is sent successfully, false otherwise.
2073     */
2074    public boolean setLogLevel(boolean turnOnVerbose) {
2075        synchronized (mLock) {
2076            int logLevel = turnOnVerbose
2077                    ? ISupplicant.DebugLevel.DEBUG
2078                    : ISupplicant.DebugLevel.INFO;
2079            return setDebugParams(logLevel, false, false);
2080        }
2081    }
2082
2083    /** See ISupplicant.hal for documentation */
2084    private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
2085        synchronized (mLock) {
2086            final String methodStr = "setDebugParams";
2087            if (!checkSupplicantAndLogFailure(methodStr)) return false;
2088            try {
2089                SupplicantStatus status =
2090                        mISupplicant.setDebugParams(level, showTimestamp, showKeys);
2091                return checkStatusAndLogFailure(status, methodStr);
2092            } catch (RemoteException e) {
2093                handleRemoteException(e, methodStr);
2094                return false;
2095            }
2096        }
2097    }
2098
2099    /**
2100     * Set concurrency priority between P2P & STA operations.
2101     *
2102     * @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
2103     *                            false otherwise.
2104     * @return true if request is sent successfully, false otherwise.
2105     */
2106    public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
2107        synchronized (mLock) {
2108            if (isStaHigherPriority) {
2109                return setConcurrencyPriority(IfaceType.STA);
2110            } else {
2111                return setConcurrencyPriority(IfaceType.P2P);
2112            }
2113        }
2114    }
2115
2116    /** See ISupplicant.hal for documentation */
2117    private boolean setConcurrencyPriority(int type) {
2118        synchronized (mLock) {
2119            final String methodStr = "setConcurrencyPriority";
2120            if (!checkSupplicantAndLogFailure(methodStr)) return false;
2121            try {
2122                SupplicantStatus status = mISupplicant.setConcurrencyPriority(type);
2123                return checkStatusAndLogFailure(status, methodStr);
2124            } catch (RemoteException e) {
2125                handleRemoteException(e, methodStr);
2126                return false;
2127            }
2128        }
2129    }
2130
2131    /**
2132     * Returns false if Supplicant is null, and logs failure to call methodStr
2133     */
2134    private boolean checkSupplicantAndLogFailure(final String methodStr) {
2135        synchronized (mLock) {
2136            if (mISupplicant == null) {
2137                Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
2138                return false;
2139            }
2140            return true;
2141        }
2142    }
2143
2144    /**
2145     * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
2146     */
2147    private ISupplicantStaIface checkSupplicantStaIfaceAndLogFailure(
2148            @NonNull String ifaceName, final String methodStr) {
2149        synchronized (mLock) {
2150            ISupplicantStaIface iface = getStaIface(ifaceName);
2151            if (iface == null) {
2152                Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
2153                return null;
2154            }
2155            return iface;
2156        }
2157    }
2158
2159    /**
2160     * Returns false if SupplicantStaNetwork is null, and logs failure to call methodStr
2161     */
2162    private SupplicantStaNetworkHal checkSupplicantStaNetworkAndLogFailure(
2163            @NonNull String ifaceName, final String methodStr) {
2164        synchronized (mLock) {
2165            SupplicantStaNetworkHal networkHal = getCurrentNetworkRemoteHandle(ifaceName);
2166            if (networkHal == null) {
2167                Log.e(TAG, "Can't call " + methodStr + ", SupplicantStaNetwork is null");
2168                return null;
2169            }
2170            return networkHal;
2171        }
2172    }
2173
2174    /**
2175     * Returns true if provided status code is SUCCESS, logs debug message and returns false
2176     * otherwise
2177     */
2178    private boolean checkStatusAndLogFailure(SupplicantStatus status,
2179            final String methodStr) {
2180        synchronized (mLock) {
2181            if (status.code != SupplicantStatusCode.SUCCESS) {
2182                Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: " + status);
2183                return false;
2184            } else {
2185                if (mVerboseLoggingEnabled) {
2186                    Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
2187                }
2188                return true;
2189            }
2190        }
2191    }
2192
2193    /**
2194     * Helper function to log callbacks.
2195     */
2196    private void logCallback(final String methodStr) {
2197        synchronized (mLock) {
2198            if (mVerboseLoggingEnabled) {
2199                Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
2200            }
2201        }
2202    }
2203
2204
2205    private void handleRemoteException(RemoteException e, String methodStr) {
2206        synchronized (mLock) {
2207            clearState();
2208            Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
2209        }
2210    }
2211
2212    /**
2213     * Converts the Wps config method string to the equivalent enum value.
2214     */
2215    private static short stringToWpsConfigMethod(String configMethod) {
2216        switch (configMethod) {
2217            case "usba":
2218                return WpsConfigMethods.USBA;
2219            case "ethernet":
2220                return WpsConfigMethods.ETHERNET;
2221            case "label":
2222                return WpsConfigMethods.LABEL;
2223            case "display":
2224                return WpsConfigMethods.DISPLAY;
2225            case "int_nfc_token":
2226                return WpsConfigMethods.INT_NFC_TOKEN;
2227            case "ext_nfc_token":
2228                return WpsConfigMethods.EXT_NFC_TOKEN;
2229            case "nfc_interface":
2230                return WpsConfigMethods.NFC_INTERFACE;
2231            case "push_button":
2232                return WpsConfigMethods.PUSHBUTTON;
2233            case "keypad":
2234                return WpsConfigMethods.KEYPAD;
2235            case "virtual_push_button":
2236                return WpsConfigMethods.VIRT_PUSHBUTTON;
2237            case "physical_push_button":
2238                return WpsConfigMethods.PHY_PUSHBUTTON;
2239            case "p2ps":
2240                return WpsConfigMethods.P2PS;
2241            case "virtual_display":
2242                return WpsConfigMethods.VIRT_DISPLAY;
2243            case "physical_display":
2244                return WpsConfigMethods.PHY_DISPLAY;
2245            default:
2246                throw new IllegalArgumentException(
2247                        "Invalid WPS config method: " + configMethod);
2248        }
2249    }
2250
2251    /**
2252     * Converts the supplicant state received from HIDL to the equivalent framework state.
2253     */
2254    private static SupplicantState supplicantHidlStateToFrameworkState(int state) {
2255        switch (state) {
2256            case ISupplicantStaIfaceCallback.State.DISCONNECTED:
2257                return SupplicantState.DISCONNECTED;
2258            case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
2259                return SupplicantState.INTERFACE_DISABLED;
2260            case ISupplicantStaIfaceCallback.State.INACTIVE:
2261                return SupplicantState.INACTIVE;
2262            case ISupplicantStaIfaceCallback.State.SCANNING:
2263                return SupplicantState.SCANNING;
2264            case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
2265                return SupplicantState.AUTHENTICATING;
2266            case ISupplicantStaIfaceCallback.State.ASSOCIATING:
2267                return SupplicantState.ASSOCIATING;
2268            case ISupplicantStaIfaceCallback.State.ASSOCIATED:
2269                return SupplicantState.ASSOCIATED;
2270            case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
2271                return SupplicantState.FOUR_WAY_HANDSHAKE;
2272            case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
2273                return SupplicantState.GROUP_HANDSHAKE;
2274            case ISupplicantStaIfaceCallback.State.COMPLETED:
2275                return SupplicantState.COMPLETED;
2276            default:
2277                throw new IllegalArgumentException("Invalid state: " + state);
2278        }
2279    }
2280
2281    private class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub {
2282        private String mIfaceName;
2283        private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch
2284
2285        SupplicantStaIfaceHalCallback(@NonNull String ifaceName) {
2286            mIfaceName = ifaceName;
2287        }
2288
2289        /**
2290         * Parses the provided payload into an ANQP element.
2291         *
2292         * @param infoID  Element type.
2293         * @param payload Raw payload bytes.
2294         * @return AnqpElement instance on success, null on failure.
2295         */
2296        private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
2297                                             ArrayList<Byte> payload) {
2298            synchronized (mLock) {
2299                try {
2300                    return Constants.getANQPElementID(infoID) != null
2301                            ? ANQPParser.parseElement(
2302                            infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
2303                            : ANQPParser.parseHS20Element(
2304                            infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
2305                } catch (IOException | BufferUnderflowException e) {
2306                    Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
2307                    return null;
2308                }
2309            }
2310        }
2311
2312        /**
2313         * Parse the ANQP element data and add to the provided elements map if successful.
2314         *
2315         * @param elementsMap Map to add the parsed out element to.
2316         * @param infoID  Element type.
2317         * @param payload Raw payload bytes.
2318         */
2319        private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
2320                                         Constants.ANQPElementType infoID,
2321                                         ArrayList<Byte> payload) {
2322            synchronized (mLock) {
2323                if (payload == null || payload.isEmpty()) return;
2324                ANQPElement element = parseAnqpElement(infoID, payload);
2325                if (element != null) {
2326                    elementsMap.put(infoID, element);
2327                }
2328            }
2329        }
2330
2331        @Override
2332        public void onNetworkAdded(int id) {
2333            synchronized (mLock) {
2334                logCallback("onNetworkAdded");
2335            }
2336        }
2337
2338        @Override
2339        public void onNetworkRemoved(int id) {
2340            synchronized (mLock) {
2341                logCallback("onNetworkRemoved");
2342            }
2343        }
2344
2345        @Override
2346        public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
2347                                   ArrayList<Byte> ssid) {
2348            synchronized (mLock) {
2349                logCallback("onStateChanged");
2350                SupplicantState newSupplicantState = supplicantHidlStateToFrameworkState(newState);
2351                WifiSsid wifiSsid =
2352                        WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
2353                String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
2354                mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
2355                if (newSupplicantState == SupplicantState.COMPLETED) {
2356                    mWifiMonitor.broadcastNetworkConnectionEvent(
2357                            mIfaceName, getCurrentNetworkId(mIfaceName), bssidStr);
2358                }
2359                mWifiMonitor.broadcastSupplicantStateChangeEvent(
2360                        mIfaceName, getCurrentNetworkId(mIfaceName), wifiSsid,
2361                        bssidStr, newSupplicantState);
2362            }
2363        }
2364
2365        @Override
2366        public void onAnqpQueryDone(byte[/* 6 */] bssid,
2367                                    ISupplicantStaIfaceCallback.AnqpData data,
2368                                    ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
2369            synchronized (mLock) {
2370                logCallback("onAnqpQueryDone");
2371                Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
2372                addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
2373                addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
2374                addAnqpElementToMap(
2375                        elementsMap, ANQPIPAddrAvailability, data.ipAddrTypeAvailability);
2376                addAnqpElementToMap(elementsMap, ANQPNAIRealm, data.naiRealm);
2377                addAnqpElementToMap(elementsMap, ANQP3GPPNetwork, data.anqp3gppCellularNetwork);
2378                addAnqpElementToMap(elementsMap, ANQPDomName, data.domainName);
2379                addAnqpElementToMap(elementsMap, HSFriendlyName, hs20Data.operatorFriendlyName);
2380                addAnqpElementToMap(elementsMap, HSWANMetrics, hs20Data.wanMetrics);
2381                addAnqpElementToMap(elementsMap, HSConnCapability, hs20Data.connectionCapability);
2382                addAnqpElementToMap(elementsMap, HSOSUProviders, hs20Data.osuProvidersList);
2383                mWifiMonitor.broadcastAnqpDoneEvent(
2384                        mIfaceName, new AnqpEvent(NativeUtil.macAddressToLong(bssid), elementsMap));
2385            }
2386        }
2387
2388        @Override
2389        public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
2390                                        ArrayList<Byte> data) {
2391            synchronized (mLock) {
2392                logCallback("onHs20IconQueryDone");
2393                mWifiMonitor.broadcastIconDoneEvent(
2394                        mIfaceName,
2395                        new IconEvent(NativeUtil.macAddressToLong(bssid), fileName, data.size(),
2396                                NativeUtil.byteArrayFromArrayList(data)));
2397            }
2398        }
2399
2400        @Override
2401        public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
2402            synchronized (mLock) {
2403                logCallback("onHs20SubscriptionRemediation");
2404                mWifiMonitor.broadcastWnmEvent(
2405                        mIfaceName,
2406                        new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod));
2407            }
2408        }
2409
2410        @Override
2411        public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
2412                                               int reAuthDelayInSec, String url) {
2413            synchronized (mLock) {
2414                logCallback("onHs20DeauthImminentNotice");
2415                mWifiMonitor.broadcastWnmEvent(
2416                        mIfaceName,
2417                        new WnmData(NativeUtil.macAddressToLong(bssid), url,
2418                                reasonCode == WnmData.ESS, reAuthDelayInSec));
2419            }
2420        }
2421
2422        @Override
2423        public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
2424            synchronized (mLock) {
2425                logCallback("onDisconnected");
2426                if (mVerboseLoggingEnabled) {
2427                    Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway
2428                            + " locallyGenerated=" + locallyGenerated
2429                            + " reasonCode=" + reasonCode);
2430                }
2431                if (mStateIsFourway
2432                        && (!locallyGenerated || reasonCode != ReasonCode.IE_IN_4WAY_DIFFERS)) {
2433                    mWifiMonitor.broadcastAuthenticationFailureEvent(
2434                            mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1);
2435                }
2436                mWifiMonitor.broadcastNetworkDisconnectionEvent(
2437                        mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
2438                        NativeUtil.macAddressFromByteArray(bssid));
2439            }
2440        }
2441
2442        @Override
2443        public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) {
2444            synchronized (mLock) {
2445                logCallback("onAssociationRejected");
2446                mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode, timedOut,
2447                        NativeUtil.macAddressFromByteArray(bssid));
2448            }
2449        }
2450
2451        @Override
2452        public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
2453            synchronized (mLock) {
2454                logCallback("onAuthenticationTimeout");
2455                mWifiMonitor.broadcastAuthenticationFailureEvent(
2456                        mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1);
2457            }
2458        }
2459
2460        @Override
2461        public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
2462            synchronized (mLock) {
2463                logCallback("onBssidChanged");
2464                if (reason == BssidChangeReason.ASSOC_START) {
2465                    mWifiMonitor.broadcastTargetBssidEvent(
2466                            mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
2467                } else if (reason == BssidChangeReason.ASSOC_COMPLETE) {
2468                    mWifiMonitor.broadcastAssociatedBssidEvent(
2469                            mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
2470                }
2471            }
2472        }
2473
2474        @Override
2475        public void onEapFailure() {
2476            synchronized (mLock) {
2477                logCallback("onEapFailure");
2478                mWifiMonitor.broadcastAuthenticationFailureEvent(
2479                        mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, -1);
2480            }
2481        }
2482
2483        @Override
2484        public void onWpsEventSuccess() {
2485            logCallback("onWpsEventSuccess");
2486            synchronized (mLock) {
2487                mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName);
2488            }
2489        }
2490
2491        @Override
2492        public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
2493            synchronized (mLock) {
2494                logCallback("onWpsEventFail");
2495                if (configError == WpsConfigError.MSG_TIMEOUT
2496                        && errorInd == WpsErrorIndication.NO_ERROR) {
2497                    mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
2498                } else {
2499                    mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd);
2500                }
2501            }
2502        }
2503
2504        @Override
2505        public void onWpsEventPbcOverlap() {
2506            synchronized (mLock) {
2507                logCallback("onWpsEventPbcOverlap");
2508                mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
2509            }
2510        }
2511
2512        @Override
2513        public void onExtRadioWorkStart(int id) {
2514            synchronized (mLock) {
2515                logCallback("onExtRadioWorkStart");
2516            }
2517        }
2518
2519        @Override
2520        public void onExtRadioWorkTimeout(int id) {
2521            synchronized (mLock) {
2522                logCallback("onExtRadioWorkTimeout");
2523            }
2524        }
2525    }
2526
2527    private class SupplicantStaIfaceHalCallbackV1_1 extends
2528            android.hardware.wifi.supplicant.V1_1.ISupplicantStaIfaceCallback.Stub {
2529        private String mIfaceName;
2530        private SupplicantStaIfaceHalCallback mCallbackV1_0;
2531
2532        SupplicantStaIfaceHalCallbackV1_1(@NonNull String ifaceName,
2533                @NonNull SupplicantStaIfaceHalCallback callback) {
2534            mIfaceName = ifaceName;
2535            mCallbackV1_0 = callback;
2536        }
2537
2538        @Override
2539        public void onNetworkAdded(int id) {
2540            mCallbackV1_0.onNetworkAdded(id);
2541        }
2542
2543        @Override
2544        public void onNetworkRemoved(int id) {
2545            mCallbackV1_0.onNetworkRemoved(id);
2546        }
2547
2548        @Override
2549        public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
2550                                   ArrayList<Byte> ssid) {
2551            mCallbackV1_0.onStateChanged(newState, bssid, id, ssid);
2552        }
2553
2554        @Override
2555        public void onAnqpQueryDone(byte[/* 6 */] bssid,
2556                                    ISupplicantStaIfaceCallback.AnqpData data,
2557                                    ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
2558            mCallbackV1_0.onAnqpQueryDone(bssid, data, hs20Data);
2559        }
2560
2561        @Override
2562        public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
2563                                        ArrayList<Byte> data) {
2564            mCallbackV1_0.onHs20IconQueryDone(bssid, fileName, data);
2565        }
2566
2567        @Override
2568        public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid,
2569                                                  byte osuMethod, String url) {
2570            mCallbackV1_0.onHs20SubscriptionRemediation(bssid, osuMethod, url);
2571        }
2572
2573        @Override
2574        public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
2575                                               int reAuthDelayInSec, String url) {
2576            mCallbackV1_0.onHs20DeauthImminentNotice(bssid, reasonCode, reAuthDelayInSec, url);
2577        }
2578
2579        @Override
2580        public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated,
2581                                   int reasonCode) {
2582            mCallbackV1_0.onDisconnected(bssid, locallyGenerated, reasonCode);
2583        }
2584
2585        @Override
2586        public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode,
2587                                          boolean timedOut) {
2588            mCallbackV1_0.onAssociationRejected(bssid, statusCode, timedOut);
2589        }
2590
2591        @Override
2592        public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
2593            mCallbackV1_0.onAuthenticationTimeout(bssid);
2594        }
2595
2596        @Override
2597        public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
2598            mCallbackV1_0.onBssidChanged(reason, bssid);
2599        }
2600
2601        @Override
2602        public void onEapFailure() {
2603            mCallbackV1_0.onEapFailure();
2604        }
2605
2606        @Override
2607        public void onEapFailure_1_1(int code) {
2608            synchronized (mLock) {
2609                logCallback("onEapFailure_1_1");
2610                mWifiMonitor.broadcastAuthenticationFailureEvent(
2611                        mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, code);
2612            }
2613        }
2614
2615        @Override
2616        public void onWpsEventSuccess() {
2617            mCallbackV1_0.onWpsEventSuccess();
2618        }
2619
2620        @Override
2621        public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
2622            mCallbackV1_0.onWpsEventFail(bssid, configError, errorInd);
2623        }
2624
2625        @Override
2626        public void onWpsEventPbcOverlap() {
2627            mCallbackV1_0.onWpsEventPbcOverlap();
2628        }
2629
2630        @Override
2631        public void onExtRadioWorkStart(int id) {
2632            mCallbackV1_0.onExtRadioWorkStart(id);
2633        }
2634
2635        @Override
2636        public void onExtRadioWorkTimeout(int id) {
2637            mCallbackV1_0.onExtRadioWorkTimeout(id);
2638        }
2639    }
2640
2641    private static void logd(String s) {
2642        Log.d(TAG, s);
2643    }
2644
2645    private static void logi(String s) {
2646        Log.i(TAG, s);
2647    }
2648
2649    private static void loge(String s) {
2650        Log.e(TAG, s);
2651    }
2652}
2653