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