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