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