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