SupplicantStaIfaceHal.java revision 5f39baacf16b55c5551574bd1d973cdb14f70c45
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 android.content.Context;
19import android.hardware.wifi.supplicant.V1_0.ISupplicant;
20import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
21import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
22import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
23import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
24import android.hardware.wifi.supplicant.V1_0.IfaceType;
25import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
26import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
27import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
28import android.hidl.manager.V1_0.IServiceManager;
29import android.hidl.manager.V1_0.IServiceNotification;
30import android.net.IpConfiguration;
31import android.net.wifi.WifiConfiguration;
32import android.os.RemoteException;
33import android.util.Log;
34import android.util.SparseArray;
35
36import com.android.server.wifi.util.NativeUtil;
37
38import java.nio.ByteBuffer;
39import java.nio.ByteOrder;
40import java.util.ArrayList;
41import java.util.HashMap;
42import java.util.List;
43import java.util.Map;
44import java.util.regex.Matcher;
45import java.util.regex.Pattern;
46
47/**
48 * Hal calls for bring up/shut down of the supplicant daemon and for
49 * sending requests to the supplicant daemon
50 */
51public class SupplicantStaIfaceHal {
52    private static final boolean DBG = false;
53    private static final String TAG = "SupplicantStaIfaceHal";
54    private static final String SERVICE_MANAGER_NAME = "manager";
55    /**
56     * Regex pattern for extracting the wps device type bytes.
57     * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
58     */
59    private static final Pattern WPS_DEVICE_TYPE_PATTERN =
60            Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
61
62    private IServiceManager mIServiceManager = null;
63    // Supplicant HAL interface objects
64    private ISupplicant mISupplicant;
65    private ISupplicantStaIface mISupplicantStaIface;
66    // Currently configured network in wpa_supplicant
67    private SupplicantStaNetworkHal mCurrentNetwork;
68    // Currently configured network's framework network Id.
69    private int mFrameworkNetworkId;
70    private final Object mLock = new Object();
71    private final Context mContext;
72    private final WifiMonitor mWifiMonitor;
73
74    public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) {
75        mContext = context;
76        mWifiMonitor = monitor;
77    }
78
79    /**
80     * Registers a service notification for the ISupplicant service, which triggers intialization of
81     * the ISupplicantStaIface
82     * @return true if the service notification was successfully registered
83     */
84    public boolean initialize() {
85        if (DBG) Log.i(TAG, "Registering ISupplicant service ready callback.");
86        synchronized (mLock) {
87            mISupplicant = null;
88            mISupplicantStaIface = null;
89            if (mIServiceManager != null) {
90                // Already have an IServiceManager and serviceNotification registered, don't
91                // don't register another.
92                return true;
93            }
94            try {
95                mIServiceManager = getServiceManagerMockable();
96                if (mIServiceManager == null) {
97                    Log.e(TAG, "Failed to get HIDL Service Manager");
98                    return false;
99                }
100                if (!mIServiceManager.linkToDeath(cookie -> {
101                    Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
102                    synchronized (mLock) {
103                        supplicantServiceDiedHandler();
104                        mIServiceManager = null; // Will need to register a new ServiceNotification
105                    }
106                }, 0)) {
107                    Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
108                    supplicantServiceDiedHandler();
109                    mIServiceManager = null; // Will need to register a new ServiceNotification
110                    return false;
111                }
112                IServiceNotification serviceNotificationCb = new IServiceNotification.Stub() {
113                    public void onRegistration(String fqName, String name, boolean preexisting) {
114                        synchronized (mLock) {
115                            if (DBG) {
116                                Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
117                                        + ", " + name + " preexisting=" + preexisting);
118                            }
119                            if (!initSupplicantService() || !initSupplicantStaIface()) {
120                                Log.e(TAG, "initalizing ISupplicantIfaces failed.");
121                                supplicantServiceDiedHandler();
122                            } else {
123                                Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
124                            }
125                        }
126                    }
127                };
128                /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
129                   exists */
130                if (!mIServiceManager.registerForNotifications(ISupplicant.kInterfaceName,
131                        "", serviceNotificationCb)) {
132                    Log.e(TAG, "Failed to register for notifications to "
133                            + ISupplicant.kInterfaceName);
134                    mIServiceManager = null; // Will need to register a new ServiceNotification
135                    return false;
136                }
137            } catch (RemoteException e) {
138                Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
139                        + e);
140                supplicantServiceDiedHandler();
141            }
142            return true;
143        }
144    }
145
146    private boolean initSupplicantService() {
147        synchronized (mLock) {
148            try {
149                mISupplicant = getSupplicantMockable();
150            } catch (RemoteException e) {
151                Log.e(TAG, "ISupplicant.getService exception: " + e);
152                return false;
153            }
154            if (mISupplicant == null) {
155                Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
156                return false;
157            }
158        }
159        return true;
160    }
161
162    private boolean initSupplicantStaIface() {
163        synchronized (mLock) {
164            /** List all supplicant Ifaces */
165            final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
166            try {
167                mISupplicant.listInterfaces((SupplicantStatus status,
168                        ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
169                    if (status.code != SupplicantStatusCode.SUCCESS) {
170                        Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
171                        return;
172                    }
173                    supplicantIfaces.addAll(ifaces);
174                });
175            } catch (RemoteException e) {
176                Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
177                return false;
178            }
179            if (supplicantIfaces.size() == 0) {
180                Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
181                return false;
182            }
183            Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
184            for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
185                if (ifaceInfo.type == IfaceType.STA) {
186                    try {
187                        mISupplicant.getInterface(ifaceInfo,
188                                (SupplicantStatus status, ISupplicantIface iface) -> {
189                                if (status.code != SupplicantStatusCode.SUCCESS) {
190                                    Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
191                                    return;
192                                }
193                                supplicantIface.value = iface;
194                            });
195                    } catch (RemoteException e) {
196                        Log.e(TAG, "ISupplicant.getInterface exception: " + e);
197                        return false;
198                    }
199                    break;
200                }
201            }
202            if (supplicantIface.value == null) {
203                Log.e(TAG, "initSupplicantStaIface got null iface");
204                return false;
205            }
206            mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value);
207            return true;
208        }
209    }
210
211    private void supplicantServiceDiedHandler() {
212        synchronized (mLock) {
213            mISupplicant = null;
214            mISupplicantStaIface = null;
215        }
216    }
217
218    /**
219     * Signals whether Initialization completed successfully. Only necessary for testing, is not
220     * needed to guard calls etc.
221     */
222    public boolean isInitializationComplete() {
223        return mISupplicantStaIface != null;
224    }
225
226    /**
227     * Wrapper functions to access static HAL methods, created to be mockable in unit tests
228     */
229    protected IServiceManager getServiceManagerMockable() throws RemoteException {
230        return IServiceManager.getService(SERVICE_MANAGER_NAME);
231    }
232
233    protected ISupplicant getSupplicantMockable() throws RemoteException {
234        return ISupplicant.getService();
235    }
236
237    protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
238        return ISupplicantStaIface.asInterface(iface.asBinder());
239    }
240
241    /**
242     * Add a network configuration to wpa_supplicant.
243     *
244     * @param config Config corresponding to the network.
245     * @return SupplicantStaNetwork of the added network in wpa_supplicant.
246     */
247    private SupplicantStaNetworkHal addNetwork(WifiConfiguration config) {
248        logi("addSupplicantStaNetwork via HIDL");
249        if (config == null) {
250            loge("Cannot add NULL network!");
251            return null;
252        }
253        SupplicantStaNetworkHal network = addNetwork();
254        if (network == null) {
255            loge("Failed to add a network!");
256            return null;
257        }
258        if (network.saveWifiConfiguration(config)) {
259            return network;
260        } else {
261            loge("Failed to save variables for: " + config.configKey());
262            return null;
263        }
264    }
265
266    /**
267     * Add the provided network configuration to wpa_supplicant and initiate connection to it.
268     * This method does the following:
269     * 1. Triggers disconnect command to wpa_supplicant (if |shouldDisconnect| is true).
270     * 2. Remove any existing network in wpa_supplicant.
271     * 3. Add a new network to wpa_supplicant.
272     * 4. Save the provided configuration to wpa_supplicant.
273     * 5. Select the new network in wpa_supplicant.
274     *
275     * @param config WifiConfiguration parameters for the provided network.
276     * @param shouldDisconnect whether to trigger a disconnection or not.
277     * @return {@code true} if it succeeds, {@code false} otherwise
278     */
279    public boolean connectToNetwork(WifiConfiguration config, boolean shouldDisconnect) {
280        mFrameworkNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
281        mCurrentNetwork = null;
282        logd("connectToNetwork " + config.configKey()
283                + " (shouldDisconnect " + shouldDisconnect + ")");
284        if (shouldDisconnect && !disconnect()) {
285            loge("Failed to trigger disconnect");
286            return false;
287        }
288        if (!removeAllNetworks()) {
289            loge("Failed to remove existing networks");
290            return false;
291        }
292        mCurrentNetwork = addNetwork(config);
293        if (mCurrentNetwork == null) {
294            loge("Failed to add/save network configuration: " + config.configKey());
295            return false;
296        }
297        if (!mCurrentNetwork.select()) {
298            loge("Failed to select network configuration: " + config.configKey());
299            return false;
300        }
301        mFrameworkNetworkId = config.networkId;
302        return true;
303    }
304
305    /**
306     * Initiates roaming to the already configured network in wpa_supplicant. If the network
307     * configuration provided does not match the already configured network, then this triggers
308     * a new connection attempt (instead of roam).
309     * 1. First check if we're attempting to connect to the same network as we currently have
310     * configured.
311     * 2. Set the new bssid for the network in wpa_supplicant.
312     * 3. Trigger reassociate command to wpa_supplicant.
313     *
314     * @param config WifiConfiguration parameters for the provided network.
315     * @return {@code true} if it succeeds, {@code false} otherwise
316     */
317    public boolean roamToNetwork(WifiConfiguration config) {
318        if (mFrameworkNetworkId != config.networkId || mCurrentNetwork == null) {
319            Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
320                    + "Current network ID: " + mFrameworkNetworkId);
321            return connectToNetwork(config, false);
322        }
323        String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
324        logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
325        if (!mCurrentNetwork.setBssid(bssid)) {
326            loge("Failed to set new bssid on network: " + config.configKey());
327            return false;
328        }
329        if (!reassociate()) {
330            loge("Failed to trigger reassociate");
331            return false;
332        }
333        return true;
334    }
335
336    /**
337     * Load all the configured networks from wpa_supplicant.
338     *
339     * @param configs       Map of configuration key to configuration objects corresponding to all
340     *                      the networks.
341     * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
342     * @return true if succeeds, false otherwise.
343     */
344    public boolean loadNetworks(Map<String, WifiConfiguration> configs,
345                                SparseArray<Map<String, String>> networkExtras) {
346        List<Integer> networkIds = listNetworks();
347        if (networkIds == null) {
348            Log.e(TAG, "Failed to list networks");
349            return false;
350        }
351        for (Integer networkId : networkIds) {
352            SupplicantStaNetworkHal network = getNetwork(networkId);
353            if (network == null) {
354                Log.e(TAG, "Failed to get network with ID: " + networkId);
355                return false;
356            }
357            WifiConfiguration config = new WifiConfiguration();
358            Map<String, String> networkExtra = new HashMap<>();
359            if (!network.loadWifiConfiguration(config, networkExtra)) {
360                Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId);
361                return false;
362            }
363            // Set the default IP assignments.
364            config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
365            config.setProxySettings(IpConfiguration.ProxySettings.NONE);
366
367            networkExtras.put(networkId, networkExtra);
368            String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
369            final WifiConfiguration duplicateConfig = configs.put(configKey, config);
370            if (duplicateConfig != null) {
371                // The network is already known. Overwrite the duplicate entry.
372                Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
373                removeNetwork(duplicateConfig.networkId);
374                networkExtras.remove(duplicateConfig.networkId);
375            }
376        }
377        return true;
378    }
379
380    /**
381     * Remove all networks from supplicant
382     */
383    public boolean removeAllNetworks() {
384        synchronized (mLock) {
385            ArrayList<Integer> networks = listNetworks();
386            if (networks == null) {
387                Log.e(TAG, "removeAllNetworks failed, got null networks");
388                return false;
389            }
390            for (int id : networks) {
391                if (!removeNetwork(id)) {
392                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
393                    return false;
394                }
395            }
396        }
397        return true;
398    }
399
400    /**
401     * Set the currently configured network's bssid.
402     *
403     * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
404     * @return true if succeeds, false otherwise.
405     */
406    public boolean setCurrentNetworkBssid(String bssidStr) {
407        if (mCurrentNetwork == null) return false;
408        return mCurrentNetwork.setBssid(bssidStr);
409    }
410
411    /**
412     * Get the currently configured network's WPS NFC token.
413     *
414     * @return Hex string corresponding to the WPS NFC token.
415     */
416    public String getCurrentNetworkWpsNfcConfigurationToken() {
417        if (mCurrentNetwork == null) return null;
418        return mCurrentNetwork.getWpsNfcConfigurationToken();
419    }
420
421    /**
422     * Gets the interface name.
423     *
424     * @return returns the name of Iface or null if the call fails
425     */
426    private String getName() {
427        synchronized (mLock) {
428            final String methodStr = "getName";
429            if (DBG) Log.i(TAG, methodStr);
430            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
431            final Mutable<String> gotName = new Mutable<>();
432            try {
433                mISupplicantStaIface.getName((SupplicantStatus status, String name) -> {
434                    if (checkStatusAndLogFailure(status, methodStr)) {
435                        gotName.value = name;
436
437                    }
438                });
439            } catch (RemoteException e) {
440                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
441                supplicantServiceDiedHandler();
442            }
443            return gotName.value;
444        }
445    }
446
447    /**
448     * Adds a new network.
449     *
450     * @return The ISupplicantNetwork object for the new network, or null if the call fails
451     */
452    private SupplicantStaNetworkHal addNetwork() {
453        synchronized (mLock) {
454            final String methodStr = "addNetwork";
455            if (DBG) Log.i(TAG, methodStr);
456            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
457            Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
458            try {
459                mISupplicantStaIface.addNetwork((SupplicantStatus status,
460                        ISupplicantNetwork network) -> {
461                    if (checkStatusAndLogFailure(status, methodStr)) {
462                        newNetwork.value = network;
463                    }
464                });
465            } catch (RemoteException e) {
466                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
467                supplicantServiceDiedHandler();
468            }
469            if (newNetwork.value != null) {
470                return getStaNetworkMockable(
471                        ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
472            } else {
473                return null;
474            }
475        }
476    }
477
478    /**
479     * Remove network from supplicant with network Id
480     *
481     * @return true if request is sent successfully, false otherwise.
482     */
483    private boolean removeNetwork(int id) {
484        synchronized (mLock) {
485            final String methodStr = "removeNetwork";
486            if (DBG) Log.i(TAG, methodStr);
487            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
488            try {
489                SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
490                return checkStatusAndLogFailure(status, methodStr);
491            } catch (RemoteException e) {
492                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
493                supplicantServiceDiedHandler();
494                return false;
495            }
496        }
497    }
498
499    /**
500     * Use this to mock the creation of SupplicantStaNetworkHal instance.
501     *
502     * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
503     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
504     * the call fails
505     */
506    protected SupplicantStaNetworkHal getStaNetworkMockable(
507            ISupplicantStaNetwork iSupplicantStaNetwork) {
508        return new SupplicantStaNetworkHal(iSupplicantStaNetwork, mContext, mWifiMonitor);
509    }
510
511    /**
512     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
513     * the call fails
514     */
515    private SupplicantStaNetworkHal getNetwork(int id) {
516        synchronized (mLock) {
517            final String methodStr = "getNetwork";
518            if (DBG) Log.i(TAG, methodStr);
519            Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
520            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
521            try {
522                mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
523                        ISupplicantNetwork network) -> {
524                    if (checkStatusAndLogFailure(status, methodStr)) {
525                        gotNetwork.value = network;
526                    }
527                });
528            } catch (RemoteException e) {
529                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
530                supplicantServiceDiedHandler();
531            }
532            if (gotNetwork.value != null) {
533                return getStaNetworkMockable(
534                        ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
535            } else {
536                return null;
537            }
538        }
539    }
540
541    /**
542     * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
543     * null if the call fails
544     */
545    private java.util.ArrayList<Integer> listNetworks() {
546        synchronized (mLock) {
547            final String methodStr = "listNetworks";
548            if (DBG) Log.i(TAG, methodStr);
549            Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
550            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
551            try {
552                mISupplicantStaIface.listNetworks((SupplicantStatus status,
553                        java.util.ArrayList<Integer> networkIds) -> {
554                    if (checkStatusAndLogFailure(status, methodStr)) {
555                        networkIdList.value = networkIds;
556                    }
557                });
558            } catch (RemoteException e) {
559                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
560                supplicantServiceDiedHandler();
561            }
562            return networkIdList.value;
563        }
564    }
565
566    /**
567     * Set WPS device name.
568     *
569     * @param name String to be set.
570     * @return true if request is sent successfully, false otherwise.
571     */
572    public boolean setWpsDeviceName(String name) {
573        synchronized (mLock) {
574            final String methodStr = "setWpsDeviceName";
575            if (DBG) Log.i(TAG, methodStr);
576            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
577            try {
578                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceName(name);
579                return checkStatusAndLogFailure(status, methodStr);
580            } catch (RemoteException e) {
581                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
582                supplicantServiceDiedHandler();
583                return false;
584            }
585        }
586    }
587
588    /**
589     * Set WPS device type.
590     *
591     * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
592     * @return true if request is sent successfully, false otherwise.
593     */
594    public boolean setWpsDeviceType(String typeStr) {
595        Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
596        if (!match.find() || match.groupCount() != 3) {
597            Log.e(TAG, "Malformed WPS device type " + typeStr);
598            return false;
599        }
600        short categ = Short.parseShort(match.group(1));
601        byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
602        short subCateg = Short.parseShort(match.group(3));
603
604        byte[] bytes = new byte[8];
605        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
606        byteBuffer.putShort(categ);
607        byteBuffer.put(oui);
608        byteBuffer.putShort(subCateg);
609        return setWpsDeviceType(bytes);
610    }
611
612    private boolean setWpsDeviceType(byte[/* 8 */] type) {
613        synchronized (mLock) {
614            final String methodStr = "setWpsDeviceType";
615            if (DBG) Log.i(TAG, methodStr);
616            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
617            try {
618                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceType(type);
619                return checkStatusAndLogFailure(status, methodStr);
620            } catch (RemoteException e) {
621                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
622                supplicantServiceDiedHandler();
623                return false;
624            }
625        }
626    }
627
628    /**
629     * Set WPS manufacturer.
630     *
631     * @param manufacturer String to be set.
632     * @return true if request is sent successfully, false otherwise.
633     */
634    public boolean setWpsManufacturer(String manufacturer) {
635        synchronized (mLock) {
636            final String methodStr = "setWpsManufacturer";
637            if (DBG) Log.i(TAG, methodStr);
638            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
639            try {
640                SupplicantStatus status = mISupplicantStaIface.setWpsManufacturer(manufacturer);
641                return checkStatusAndLogFailure(status, methodStr);
642            } catch (RemoteException e) {
643                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
644                supplicantServiceDiedHandler();
645                return false;
646            }
647        }
648    }
649
650    /**
651     * Set WPS model name.
652     *
653     * @param modelName String to be set.
654     * @return true if request is sent successfully, false otherwise.
655     */
656    public boolean setWpsModelName(String modelName) {
657        synchronized (mLock) {
658            final String methodStr = "setWpsModelName";
659            if (DBG) Log.i(TAG, methodStr);
660            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
661            try {
662                SupplicantStatus status = mISupplicantStaIface.setWpsModelName(modelName);
663                return checkStatusAndLogFailure(status, methodStr);
664            } catch (RemoteException e) {
665                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
666                supplicantServiceDiedHandler();
667                return false;
668            }
669        }
670    }
671
672    /**
673     * Set WPS model number.
674     *
675     * @param modelNumber String to be set.
676     * @return true if request is sent successfully, false otherwise.
677     */
678    public boolean setWpsModelNumber(String modelNumber) {
679        synchronized (mLock) {
680            final String methodStr = "setWpsModelNumber";
681            if (DBG) Log.i(TAG, methodStr);
682            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
683            try {
684                SupplicantStatus status = mISupplicantStaIface.setWpsModelNumber(modelNumber);
685                return checkStatusAndLogFailure(status, methodStr);
686            } catch (RemoteException e) {
687                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
688                supplicantServiceDiedHandler();
689                return false;
690            }
691        }
692    }
693
694    /**
695     * Set WPS serial number.
696     *
697     * @param serialNumber String to be set.
698     * @return true if request is sent successfully, false otherwise.
699     */
700    public boolean setWpsSerialNumber(String serialNumber) {
701        synchronized (mLock) {
702            final String methodStr = "setWpsSerialNumber";
703            if (DBG) Log.i(TAG, methodStr);
704            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
705            try {
706                SupplicantStatus status = mISupplicantStaIface.setWpsSerialNumber(serialNumber);
707                return checkStatusAndLogFailure(status, methodStr);
708            } catch (RemoteException e) {
709                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
710                supplicantServiceDiedHandler();
711                return false;
712            }
713        }
714    }
715
716    /**
717     * Set WPS config methods
718     *
719     * @param configMethodsStr List of config methods.
720     * @return true if request is sent successfully, false otherwise.
721     */
722    public boolean setWpsConfigMethods(String configMethodsStr) {
723        short configMethodsMask = 0;
724        String[] configMethodsStrArr = configMethodsStr.split("\\s+");
725        for (int i = 0; i < configMethodsStrArr.length; i++) {
726            configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
727        }
728        return setWpsConfigMethods(configMethodsMask);
729    }
730
731    private boolean setWpsConfigMethods(short configMethods) {
732        synchronized (mLock) {
733            final String methodStr = "setWpsConfigMethods";
734            if (DBG) Log.i(TAG, methodStr);
735            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
736            try {
737                SupplicantStatus status = mISupplicantStaIface.setWpsConfigMethods(configMethods);
738                return checkStatusAndLogFailure(status, methodStr);
739            } catch (RemoteException e) {
740                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
741                supplicantServiceDiedHandler();
742                return false;
743            }
744        }
745    }
746
747    /**
748     * Trigger a reassociation even if the iface is currently connected.
749     *
750     * @return true if request is sent successfully, false otherwise.
751     */
752    public boolean reassociate() {
753        synchronized (mLock) {
754            final String methodStr = "reassociate";
755            if (DBG) Log.i(TAG, methodStr);
756            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
757            try {
758                SupplicantStatus status = mISupplicantStaIface.reassociate();
759                return checkStatusAndLogFailure(status, methodStr);
760            } catch (RemoteException e) {
761                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
762                supplicantServiceDiedHandler();
763                return false;
764            }
765        }
766    }
767
768    /**
769     * Trigger a reconnection if the iface is disconnected.
770     *
771     * @return true if request is sent successfully, false otherwise.
772     */
773    public boolean reconnect() {
774        synchronized (mLock) {
775            final String methodStr = "reconnect";
776            if (DBG) Log.i(TAG, methodStr);
777            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
778            try {
779                SupplicantStatus status = mISupplicantStaIface.reconnect();
780                return checkStatusAndLogFailure(status, methodStr);
781            } catch (RemoteException e) {
782                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
783                supplicantServiceDiedHandler();
784                return false;
785            }
786        }
787    }
788
789    /**
790     * Trigger a disconnection from the currently connected network.
791     *
792     * @return true if request is sent successfully, false otherwise.
793     */
794    public boolean disconnect() {
795        synchronized (mLock) {
796            final String methodStr = "disconnect";
797            if (DBG) Log.i(TAG, methodStr);
798            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
799            try {
800                SupplicantStatus status = mISupplicantStaIface.disconnect();
801                return checkStatusAndLogFailure(status, methodStr);
802            } catch (RemoteException e) {
803                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
804                supplicantServiceDiedHandler();
805                return false;
806            }
807        }
808    }
809
810    /**
811     * Enable or disable power save mode.
812     *
813     * @param enable true to enable, false to disable.
814     * @return true if request is sent successfully, false otherwise.
815     */
816    public boolean setPowerSave(boolean enable) {
817        synchronized (mLock) {
818            final String methodStr = "setPowerSave";
819            if (DBG) Log.i(TAG, methodStr);
820            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
821            try {
822                SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
823                return checkStatusAndLogFailure(status, methodStr);
824            } catch (RemoteException e) {
825                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
826                supplicantServiceDiedHandler();
827                return false;
828            }
829        }
830    }
831
832    /**
833     * Initiate TDLS discover with the specified AP.
834     *
835     * @param macAddress MAC Address of the AP.
836     * @return true if request is sent successfully, false otherwise.
837     */
838    public boolean initiateTdlsDiscover(String macAddress) {
839        return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
840    }
841    /** See ISupplicantStaIface.hal for documentation */
842    private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
843        synchronized (mLock) {
844            final String methodStr = "initiateTdlsDiscover";
845            if (DBG) Log.i(TAG, methodStr);
846            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
847            try {
848                SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
849                return checkStatusAndLogFailure(status, methodStr);
850            } catch (RemoteException e) {
851                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
852                supplicantServiceDiedHandler();
853                return false;
854            }
855        }
856    }
857
858    /**
859     * Initiate TDLS setup with the specified AP.
860     *
861     * @param macAddress MAC Address of the AP.
862     * @return true if request is sent successfully, false otherwise.
863     */
864    public boolean initiateTdlsSetup(String macAddress) {
865        return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
866    }
867    /** See ISupplicantStaIface.hal for documentation */
868    private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
869        synchronized (mLock) {
870            final String methodStr = "initiateTdlsSetup";
871            if (DBG) Log.i(TAG, methodStr);
872            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
873            try {
874                SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
875                return checkStatusAndLogFailure(status, methodStr);
876            } catch (RemoteException e) {
877                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
878                supplicantServiceDiedHandler();
879                return false;
880            }
881        }
882    }
883
884    /**
885     * Initiate TDLS teardown with the specified AP.
886     * @param macAddress MAC Address of the AP.
887     * @return true if request is sent successfully, false otherwise.
888     */
889    public boolean initiateTdlsTeardown(String macAddress) {
890        return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
891    }
892
893    /** See ISupplicantStaIface.hal for documentation */
894    private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
895        synchronized (mLock) {
896            final String methodStr = "initiateTdlsTeardown";
897            if (DBG) Log.i(TAG, methodStr);
898            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
899            try {
900                SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
901                return checkStatusAndLogFailure(status, methodStr);
902            } catch (RemoteException e) {
903                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
904                supplicantServiceDiedHandler();
905                return false;
906            }
907        }
908    }
909
910    /**
911     * Request the specified ANQP elements |elements| from the specified AP |bssid|.
912     *
913     * @param bssid BSSID of the AP
914     * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
915     * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
916     * @return true if request is sent successfully, false otherwise.
917     */
918    public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
919                                     ArrayList<Integer> hs20SubTypes) {
920        return initiateAnqpQuery(
921                NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
922    }
923
924    /** See ISupplicantStaIface.hal for documentation */
925    private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
926            java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
927        synchronized (mLock) {
928            final String methodStr = "initiateAnqpQuery";
929            if (DBG) Log.i(TAG, methodStr);
930            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
931            try {
932                SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
933                        infoElements, subTypes);
934                return checkStatusAndLogFailure(status, methodStr);
935            } catch (RemoteException e) {
936                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
937                supplicantServiceDiedHandler();
938                return false;
939            }
940        }
941    }
942
943    /**
944     * Request the specified ANQP ICON from the specified AP |bssid|.
945     *
946     * @param bssid BSSID of the AP
947     * @param fileName Name of the file to request.
948     * @return true if request is sent successfully, false otherwise.
949     */
950    public boolean initiateHs20IconQuery(String bssid, String fileName) {
951        return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
952    }
953
954    /** See ISupplicantStaIface.hal for documentation */
955    private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
956        synchronized (mLock) {
957            final String methodStr = "initiateHs20IconQuery";
958            if (DBG) Log.i(TAG, methodStr);
959            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
960            try {
961                SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
962                        fileName);
963                return checkStatusAndLogFailure(status, methodStr);
964            } catch (RemoteException e) {
965                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
966                supplicantServiceDiedHandler();
967                return false;
968            }
969        }
970    }
971
972    /**
973     * Makes a callback to HIDL to getMacAddress from supplicant
974     *
975     * @return string containing the MAC address, or null on a failed call
976     */
977    public String getMacAddress() {
978        synchronized (mLock) {
979            final String methodStr = "getMacAddress";
980            if (DBG) Log.i(TAG, methodStr);
981            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
982            Mutable<String> gotMac = new Mutable<>();
983            try {
984                mISupplicantStaIface.getMacAddress((SupplicantStatus status,
985                        byte[/* 6 */] macAddr) -> {
986                    if (checkStatusAndLogFailure(status, methodStr)) {
987                        gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
988                    }
989                });
990            } catch (RemoteException e) {
991                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
992                supplicantServiceDiedHandler();
993            }
994            return gotMac.value;
995        }
996    }
997
998    /**
999     * Start using the added RX filters.
1000     *
1001     * @return true if request is sent successfully, false otherwise.
1002     */
1003    public boolean startRxFilter() {
1004        synchronized (mLock) {
1005            final String methodStr = "startRxFilter";
1006            if (DBG) Log.i(TAG, methodStr);
1007            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1008            try {
1009                SupplicantStatus status = mISupplicantStaIface.startRxFilter();
1010                return checkStatusAndLogFailure(status, methodStr);
1011            } catch (RemoteException e) {
1012                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1013                supplicantServiceDiedHandler();
1014                return false;
1015            }
1016        }
1017    }
1018
1019    /**
1020     * Stop using the added RX filters.
1021     *
1022     * @return true if request is sent successfully, false otherwise.
1023     */
1024    public boolean stopRxFilter() {
1025        synchronized (mLock) {
1026            final String methodStr = "stopRxFilter";
1027            if (DBG) Log.i(TAG, methodStr);
1028            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1029            try {
1030                SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
1031                return checkStatusAndLogFailure(status, methodStr);
1032            } catch (RemoteException e) {
1033                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1034                supplicantServiceDiedHandler();
1035                return false;
1036            }
1037        }
1038    }
1039
1040    public static final byte RX_FILTER_TYPE_V4_MULTICAST =
1041            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1042    public static final byte RX_FILTER_TYPE_V6_MULTICAST =
1043            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
1044    /**
1045     * Add an RX filter.
1046     *
1047     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
1048     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
1049     * @return true if request is sent successfully, false otherwise.
1050     */
1051    private boolean addRxFilter(byte type) {
1052        synchronized (mLock) {
1053            final String methodStr = "addRxFilter";
1054            if (DBG) Log.i(TAG, methodStr);
1055            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1056            try {
1057                SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
1058                return checkStatusAndLogFailure(status, methodStr);
1059            } catch (RemoteException e) {
1060                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1061                supplicantServiceDiedHandler();
1062                return false;
1063            }
1064        }
1065    }
1066
1067    /**
1068     * Remove an RX filter.
1069     *
1070     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
1071     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
1072     * @return true if request is sent successfully, false otherwise.
1073     */
1074    private boolean removeRxFilter(byte type) {
1075        synchronized (mLock) {
1076            final String methodStr = "removeRxFilter";
1077            if (DBG) Log.i(TAG, methodStr);
1078            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1079            try {
1080                SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
1081                return checkStatusAndLogFailure(status, methodStr);
1082            } catch (RemoteException e) {
1083                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1084                supplicantServiceDiedHandler();
1085                return false;
1086            }
1087        }
1088    }
1089
1090    public static final byte BT_COEX_MODE_ENABLED = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
1091    public static final byte BT_COEX_MODE_DISABLED = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
1092    public static final byte BT_COEX_MODE_SENSE = ISupplicantStaIface.BtCoexistenceMode.SENSE;
1093    /**
1094     * Set Bt co existense mode.
1095     *
1096     * @param mode one of the above {@link #BT_COEX_MODE_ENABLED}, {@link #BT_COEX_MODE_DISABLED}
1097     *             or {@link #BT_COEX_MODE_SENSE} values.
1098     * @return true if request is sent successfully, false otherwise.
1099     */
1100    public boolean setBtCoexistenceMode(byte mode) {
1101        synchronized (mLock) {
1102            final String methodStr = "setBtCoexistenceMode";
1103            if (DBG) Log.i(TAG, methodStr);
1104            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1105            try {
1106                SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
1107                return checkStatusAndLogFailure(status, methodStr);
1108            } catch (RemoteException e) {
1109                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1110                supplicantServiceDiedHandler();
1111                return false;
1112            }
1113        }
1114    }
1115
1116    /** Enable or disable BT coexistence mode.
1117     *
1118     * @param enable true to enable, false to disable.
1119     * @return true if request is sent successfully, false otherwise.
1120     */
1121    public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
1122        synchronized (mLock) {
1123            final String methodStr = "setBtCoexistenceScanModeEnabled";
1124            if (DBG) Log.i(TAG, methodStr);
1125            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1126            try {
1127                SupplicantStatus status =
1128                        mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
1129                return checkStatusAndLogFailure(status, methodStr);
1130            } catch (RemoteException e) {
1131                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1132                supplicantServiceDiedHandler();
1133                return false;
1134            }
1135        }
1136    }
1137
1138    /**
1139     * Enable or disable suspend mode optimizations.
1140     *
1141     * @param enable true to enable, false otherwise.
1142     * @return true if request is sent successfully, false otherwise.
1143     */
1144    public boolean setSuspendModeEnabled(boolean enable) {
1145        synchronized (mLock) {
1146            final String methodStr = "setSuspendModeEnabled";
1147            if (DBG) Log.i(TAG, methodStr);
1148            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1149            try {
1150                SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
1151                return checkStatusAndLogFailure(status, methodStr);
1152            } catch (RemoteException e) {
1153                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1154                supplicantServiceDiedHandler();
1155                return false;
1156            }
1157        }
1158    }
1159
1160    /**
1161     * Set country code.
1162     *
1163     * @param codeStr 2 byte ASCII string. For ex: US, CA.
1164     * @return true if request is sent successfully, false otherwise.
1165     */
1166    public boolean setCountryCode(String codeStr) {
1167        return setCountryCode(NativeUtil.stringToByteArray(codeStr));
1168    }
1169
1170    /** See ISupplicantStaIface.hal for documentation */
1171    private boolean setCountryCode(byte[/* 2 */] code) {
1172        synchronized (mLock) {
1173            final String methodStr = "setCountryCode";
1174            if (DBG) Log.i(TAG, methodStr);
1175            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1176            try {
1177                SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
1178                return checkStatusAndLogFailure(status, methodStr);
1179            } catch (RemoteException e) {
1180                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1181                supplicantServiceDiedHandler();
1182                return false;
1183            }
1184        }
1185    }
1186
1187    /**
1188     * Start WPS pin registrar operation with the specified peer and pin.
1189     *
1190     * @param bssidStr BSSID of the peer.
1191     * @param pin Pin to be used.
1192     * @return true if request is sent successfully, false otherwise.
1193     */
1194    public boolean startWpsRegistrar(String bssidStr, String pin) {
1195        return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
1196    }
1197
1198    /** See ISupplicantStaIface.hal for documentation */
1199    private boolean startWpsRegistrar(byte[/* 6 */] bssid, String pin) {
1200        synchronized (mLock) {
1201            final String methodStr = "startWpsRegistrar";
1202            if (DBG) Log.i(TAG, methodStr);
1203            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1204            try {
1205                SupplicantStatus status = mISupplicantStaIface.startWpsRegistrar(bssid, pin);
1206                return checkStatusAndLogFailure(status, methodStr);
1207            } catch (RemoteException e) {
1208                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1209                supplicantServiceDiedHandler();
1210                return false;
1211            }
1212        }
1213    }
1214
1215    /**
1216     * Start WPS pin display operation with the specified peer.
1217     *
1218     * @param bssidStr BSSID of the peer.
1219     * @return true if request is sent successfully, false otherwise.
1220     */
1221    public boolean startWpsPbc(String bssidStr) {
1222        return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
1223    }
1224
1225    /** See ISupplicantStaIface.hal for documentation */
1226    private boolean startWpsPbc(byte[/* 6 */] bssid) {
1227        synchronized (mLock) {
1228            final String methodStr = "startWpsPbc";
1229            if (DBG) Log.i(TAG, methodStr);
1230            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1231            try {
1232                SupplicantStatus status = mISupplicantStaIface.startWpsPbc(bssid);
1233                return checkStatusAndLogFailure(status, methodStr);
1234            } catch (RemoteException e) {
1235                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1236                supplicantServiceDiedHandler();
1237                return false;
1238            }
1239        }
1240    }
1241
1242    /**
1243     * Start WPS pin keypad operation with the specified pin.
1244     *
1245     * @param pin Pin to be used.
1246     * @return true if request is sent successfully, false otherwise.
1247     */
1248    public boolean startWpsPinKeypad(String pin) {
1249        synchronized (mLock) {
1250            final String methodStr = "startWpsPinKeypad";
1251            if (DBG) Log.i(TAG, methodStr);
1252            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1253            try {
1254                SupplicantStatus status = mISupplicantStaIface.startWpsPinKeypad(pin);
1255                return checkStatusAndLogFailure(status, methodStr);
1256            } catch (RemoteException e) {
1257                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1258                supplicantServiceDiedHandler();
1259                return false;
1260            }
1261        }
1262    }
1263
1264    /**
1265     * Start WPS pin display operation with the specified peer.
1266     *
1267     * @param bssidStr BSSID of the peer.
1268     * @return new pin generated on success, null otherwise.
1269     */
1270    public String startWpsPinDisplay(String bssidStr) {
1271        return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
1272    }
1273
1274    /** See ISupplicantStaIface.hal for documentation */
1275    private String startWpsPinDisplay(byte[/* 6 */] bssid) {
1276        synchronized (mLock) {
1277            final String methodStr = "startWpsPinDisplay";
1278            if (DBG) Log.i(TAG, methodStr);
1279            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
1280            final Mutable<String> gotPin = new Mutable<>();
1281            try {
1282                mISupplicantStaIface.startWpsPinDisplay(bssid,
1283                        (SupplicantStatus status, String pin) -> {
1284                            if (checkStatusAndLogFailure(status, methodStr)) {
1285                                gotPin.value = pin;
1286                            }
1287                        });
1288            } catch (RemoteException e) {
1289                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1290                supplicantServiceDiedHandler();
1291            }
1292            return gotPin.value;
1293        }
1294    }
1295
1296    /**
1297     * Cancels any ongoing WPS requests.
1298     *
1299     * @return true if request is sent successfully, false otherwise.
1300     */
1301    public boolean cancelWps() {
1302        synchronized (mLock) {
1303            final String methodStr = "cancelWps";
1304            if (DBG) Log.i(TAG, methodStr);
1305            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1306            try {
1307                SupplicantStatus status = mISupplicantStaIface.cancelWps();
1308                return checkStatusAndLogFailure(status, methodStr);
1309            } catch (RemoteException e) {
1310                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1311                supplicantServiceDiedHandler();
1312                return false;
1313            }
1314        }
1315    }
1316
1317    /**
1318     * Sets whether to use external sim for SIM/USIM processing.
1319     *
1320     * @param useExternalSim true to enable, false otherwise.
1321     * @return true if request is sent successfully, false otherwise.
1322     */
1323    public boolean setExternalSim(boolean useExternalSim) {
1324        synchronized (mLock) {
1325            final String methodStr = "setExternalSim";
1326            if (DBG) Log.i(TAG, methodStr);
1327            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
1328            try {
1329                SupplicantStatus status = mISupplicantStaIface.setExternalSim(useExternalSim);
1330                return checkStatusAndLogFailure(status, methodStr);
1331            } catch (RemoteException e) {
1332                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
1333                supplicantServiceDiedHandler();
1334                return false;
1335            }
1336        }
1337    }
1338
1339    /**
1340     * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
1341     */
1342    private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
1343        if (DBG) Log.i(TAG, methodStr);
1344        if (mISupplicantStaIface == null) {
1345            Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
1346            return false;
1347        }
1348        return true;
1349    }
1350
1351    /**
1352     * Returns true if provided status code is SUCCESS, logs debug message and returns false
1353     * otherwise
1354     */
1355    private static boolean checkStatusAndLogFailure(SupplicantStatus status,
1356            final String methodStr) {
1357        if (DBG) Log.i(TAG, methodStr);
1358        if (status.code != SupplicantStatusCode.SUCCESS) {
1359            Log.e(TAG, methodStr + " failed: " + supplicantStatusCodeToString(status.code) + ", "
1360                    + status.debugMessage);
1361            return false;
1362        }
1363        return true;
1364    }
1365
1366    /**
1367     * Converts SupplicantStatus code values to strings for debug logging
1368     * TODO(b/34811152) Remove this, or make it more break resistance
1369     */
1370    public static String supplicantStatusCodeToString(int code) {
1371        switch (code) {
1372            case 0:
1373                return "SUCCESS";
1374            case 1:
1375                return "FAILURE_UNKNOWN";
1376            case 2:
1377                return "FAILURE_ARGS_INVALID";
1378            case 3:
1379                return "FAILURE_IFACE_INVALID";
1380            case 4:
1381                return "FAILURE_IFACE_UNKNOWN";
1382            case 5:
1383                return "FAILURE_IFACE_EXISTS";
1384            case 6:
1385                return "FAILURE_IFACE_DISABLED";
1386            case 7:
1387                return "FAILURE_IFACE_NOT_DISCONNECTED";
1388            case 8:
1389                return "FAILURE_NETWORK_INVALID";
1390            case 9:
1391                return "FAILURE_NETWORK_UNKNOWN";
1392            default:
1393                return "??? UNKNOWN_CODE";
1394        }
1395    }
1396
1397
1398    /**
1399     * Converts the Wps config method string to the equivalent enum value.
1400     */
1401    private static short stringToWpsConfigMethod(String configMethod) {
1402        switch (configMethod) {
1403            case "usba":
1404                return WpsConfigMethods.USBA;
1405            case "ethernet":
1406                return WpsConfigMethods.ETHERNET;
1407            case "label":
1408                return WpsConfigMethods.LABEL;
1409            case "display":
1410                return WpsConfigMethods.DISPLAY;
1411            case "int_nfc_token":
1412                return WpsConfigMethods.INT_NFC_TOKEN;
1413            case "ext_nfc_token":
1414                return WpsConfigMethods.EXT_NFC_TOKEN;
1415            case "nfc_interface":
1416                return WpsConfigMethods.NFC_INTERFACE;
1417            case "push_button":
1418                return WpsConfigMethods.PUSHBUTTON;
1419            case "keypad":
1420                return WpsConfigMethods.KEYPAD;
1421            case "virtual_push_button":
1422                return WpsConfigMethods.VIRT_PUSHBUTTON;
1423            case "physical_push_button":
1424                return WpsConfigMethods.PHY_PUSHBUTTON;
1425            case "p2ps":
1426                return WpsConfigMethods.P2PS;
1427            case "virtual_display":
1428                return WpsConfigMethods.VIRT_DISPLAY;
1429            case "physical_display":
1430                return WpsConfigMethods.PHY_DISPLAY;
1431            default:
1432                throw new IllegalArgumentException(
1433                        "Invalid WPS config method: " + configMethod);
1434        }
1435    }
1436
1437    private static class Mutable<E> {
1438        public E value;
1439
1440        Mutable() {
1441            value = null;
1442        }
1443
1444        Mutable(E value) {
1445            this.value = value;
1446        }
1447    }
1448
1449    private void logd(String s) {
1450        Log.d(TAG, s);
1451    }
1452
1453    private void logi(String s) {
1454        Log.i(TAG, s);
1455    }
1456
1457    private void loge(String s) {
1458        Log.e(TAG, s);
1459    }
1460}
1461