SupplicantStaIfaceHal.java revision d95fa596d07855b70ff18a50a48e773155a919f5
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.hardware.wifi.supplicant.V1_0.ISupplicant;
19import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
20import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
21import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
22import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
23import android.hardware.wifi.supplicant.V1_0.IfaceType;
24import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
25import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
26import android.hidl.manager.V1_0.IServiceManager;
27import android.hidl.manager.V1_0.IServiceNotification;
28import android.net.wifi.WifiConfiguration;
29import android.os.HandlerThread;
30import android.os.RemoteException;
31import android.util.Log;
32import android.util.MutableBoolean;
33
34import com.android.server.wifi.util.NativeUtil;
35
36import java.util.ArrayList;
37
38/**
39 * Hal calls for bring up/shut down of the supplicant daemon and for
40 * sending requests to the supplicant daemon
41 */
42public class SupplicantStaIfaceHal {
43    /** Invalid Supplicant Iface type */
44    public static final int INVALID_IFACE_TYPE = -1;
45    private static final boolean DBG = false;
46    private static final String TAG = "SupplicantStaIfaceHal";
47    private static final String SERVICE_MANAGER_NAME = "manager";
48    private IServiceManager mIServiceManager = null;
49    // Supplicant HAL interface objects
50    private ISupplicant mISupplicant;
51    private ISupplicantStaIface mISupplicantStaIface;
52    private final Object mLock = new Object();
53    private final HandlerThread mHandlerThread;
54    public SupplicantStaIfaceHal(HandlerThread handlerThread) {
55        mHandlerThread = handlerThread;
56    }
57
58    /**
59     * Registers a service notification for the ISupplicant service, which triggers intialization of
60     * the ISupplicantStaIface
61     * @return true if the service notification was successfully registered
62     */
63    public boolean initialize() {
64        if (DBG) Log.i(TAG, "Registering ISupplicant service ready callback.");
65        synchronized (mLock) {
66            mISupplicant = null;
67            mISupplicantStaIface = null;
68            if (mIServiceManager != null) {
69                // Already have an IServiceManager and serviceNotification registered, don't
70                // don't register another.
71                return true;
72            }
73            try {
74                mIServiceManager = getServiceManagerMockable();
75                if (mIServiceManager == null) {
76                    Log.e(TAG, "Failed to get HIDL Service Manager");
77                    return false;
78                }
79                if (!mIServiceManager.linkToDeath(cookie -> {
80                    Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
81                    synchronized (mLock) {
82                        supplicantServiceDiedHandler();
83                        mIServiceManager = null; // Will need to register a new ServiceNotification
84                    }
85                }, 0)) {
86                    Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
87                    supplicantServiceDiedHandler();
88                    mIServiceManager = null; // Will need to register a new ServiceNotification
89                    return false;
90                }
91                IServiceNotification serviceNotificationCb = new IServiceNotification.Stub() {
92                    public void onRegistration(String fqName, String name, boolean preexisting) {
93                        synchronized (mLock) {
94                            if (DBG) {
95                                Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
96                                        + ", " + name + " preexisting=" + preexisting);
97                            }
98                            if (!initSupplicantService() || !initSupplicantStaIface()) {
99                                Log.e(TAG, "initalizing ISupplicantIfaces failed.");
100                                supplicantServiceDiedHandler();
101                            } else {
102                                Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
103                            }
104                        }
105                    }
106                };
107                /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
108                   exists */
109                if (!mIServiceManager.registerForNotifications(ISupplicant.kInterfaceName,
110                        "", serviceNotificationCb)) {
111                    Log.e(TAG, "Failed to register for notifications to "
112                            + ISupplicant.kInterfaceName);
113                    mIServiceManager = null; // Will need to register a new ServiceNotification
114                    return false;
115                }
116            } catch (RemoteException e) {
117                Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
118                        + e);
119                supplicantServiceDiedHandler();
120            }
121            return true;
122        }
123    }
124
125    private boolean initSupplicantService() {
126        synchronized (mLock) {
127            try {
128                mISupplicant = getSupplicantMockable();
129            } catch (RemoteException e) {
130                Log.e(TAG, "ISupplicant.getService exception: " + e);
131                return false;
132            }
133            if (mISupplicant == null) {
134                Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
135                return false;
136            }
137        }
138        return true;
139    }
140
141    private boolean initSupplicantStaIface() {
142        synchronized (mLock) {
143            /** List all supplicant Ifaces */
144            final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
145            try {
146                mISupplicant.listInterfaces((SupplicantStatus status,
147                        ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
148                    if (status.code != SupplicantStatusCode.SUCCESS) {
149                        Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
150                        return;
151                    }
152                    supplicantIfaces.addAll(ifaces);
153                });
154            } catch (RemoteException e) {
155                Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
156                return false;
157            }
158            if (supplicantIfaces.size() == 0) {
159                Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
160                return false;
161            }
162            Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
163            for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
164                if (ifaceInfo.type == IfaceType.STA) {
165                    try {
166                        mISupplicant.getInterface(ifaceInfo,
167                                (SupplicantStatus status, ISupplicantIface iface) -> {
168                                if (status.code != SupplicantStatusCode.SUCCESS) {
169                                    Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
170                                    return;
171                                }
172                                supplicantIface.value = iface;
173                            });
174                    } catch (RemoteException e) {
175                        Log.e(TAG, "ISupplicant.getInterface exception: " + e);
176                        return false;
177                    }
178                    break;
179                }
180            }
181            if (supplicantIface.value == null) {
182                Log.e(TAG, "initSupplicantStaIface got null iface");
183                return false;
184            }
185            mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value);
186            return true;
187        }
188    }
189
190    private void supplicantServiceDiedHandler() {
191        synchronized (mLock) {
192            mISupplicant = null;
193            mISupplicantStaIface = null;
194        }
195    }
196
197    /**
198     * Signals whether Initialization completed successfully. Only necessary for testing, is not
199     * needed to guard calls etc.
200     */
201    public boolean isInitializationComplete() {
202        return mISupplicantStaIface != null;
203    }
204
205    /**
206     * Wrapper functions to access static HAL methods, created to be mockable in unit tests
207     */
208    protected IServiceManager getServiceManagerMockable() throws RemoteException {
209        return IServiceManager.getService(SERVICE_MANAGER_NAME);
210    }
211
212    protected ISupplicant getSupplicantMockable() throws RemoteException {
213        return ISupplicant.getService();
214    }
215
216    protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
217        return ISupplicantStaIface.asInterface(iface.asBinder());
218    }
219
220    /**
221     * Add a network configuration to wpa_supplicant, via HAL
222     *
223     * @param config Config corresponding to the network.
224     * @return SupplicantStaNetwork of the added network in wpa_supplicant.
225     */
226    private SupplicantStaNetworkHal addNetwork(WifiConfiguration config) {
227        logi("addSupplicantStaNetwork via HIDL");
228        if (config == null) {
229            loge("Cannot add NULL network!");
230            return null;
231        }
232        SupplicantStaNetworkHal network = addNetwork();
233        if (network == null) {
234            loge("Failed to add a network!");
235            return null;
236        }
237        if (network.saveWifiConfiguration(config)) {
238            return network;
239        } else {
240            loge("Failed to save variables for: " + config.configKey());
241            return null;
242        }
243    }
244
245    /**
246     * Initiate connection to the provided network.
247     *
248     * @param config Config corresponding to the network.
249     * @param shouldDisconnect Whether to trigger a disconnect first.
250     * @return true if request is sent successfully, false otherwise.
251     */
252    public boolean connectToNetwork(WifiConfiguration config, boolean shouldDisconnect) {
253        logd("connectToNetwork " + config.configKey()
254                + " (shouldDisconnect " + shouldDisconnect + ")");
255        if (shouldDisconnect && !disconnect()) {
256            loge("Failed to trigger disconnect");
257            return false;
258        }
259        if (!removeAllNetworks()) {
260            loge("Failed to remove existing networks");
261            return false;
262        }
263        SupplicantStaNetworkHal network = addNetwork(config);
264        if (network == null) {
265            loge("Failed to add/save network configuration: " + config.configKey());
266            return false;
267        }
268        if (!network.getId()) {
269            loge("Failed to get network id for configuration: " + config.configKey());
270            return false;
271        }
272        if (!network.select()) {
273            loge("Failed to select network configuration: " + config.configKey());
274            return false;
275        }
276        return true;
277    }
278
279    /**
280     * Remove all networks from supplicant
281     * TODO(b/34454675) use ISupplicantIface.removeAllNetworks when it exists
282     */
283    public boolean removeAllNetworks() {
284        synchronized (mLock) {
285            ArrayList<Integer> networks = listNetworks();
286            if (networks == null) {
287                Log.e(TAG, "removeAllNetworks failed, got null networks");
288                return false;
289            }
290            for (int id : networks) {
291                if (!removeNetwork(id)) {
292                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
293                    return false;
294                }
295            }
296        }
297        return true;
298    }
299
300    /**
301     * Gets the interface name.
302     *
303     * @return returns the name of Iface or null if the call fails
304     */
305    private String getName() {
306        synchronized (mLock) {
307            MutableBoolean statusSuccess = new MutableBoolean(false);
308            final String methodStr = "getName";
309            if (DBG) Log.i(TAG, methodStr);
310            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
311            final StringBuilder builder = new StringBuilder();
312            try {
313                mISupplicantStaIface.getName((SupplicantStatus status, String name) -> {
314                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
315                    if (!statusSuccess.value) {
316                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
317                    } else {
318                        builder.append(name);
319                    }
320                });
321            } catch (RemoteException e) {
322                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
323                supplicantServiceDiedHandler();
324            }
325            if (statusSuccess.value) {
326                return builder.toString();
327            } else {
328                return null;
329            }
330        }
331    }
332
333    /**
334     * Adds a new network.
335     *
336     * @return The ISupplicantNetwork object for the new network, or null if the call fails
337     */
338    private SupplicantStaNetworkHal addNetwork() {
339        synchronized (mLock) {
340            MutableBoolean statusSuccess = new MutableBoolean(false);
341            Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
342            final String methodStr = "addNetwork";
343            if (DBG) Log.i(TAG, methodStr);
344            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
345            try {
346                mISupplicantStaIface.addNetwork((SupplicantStatus status,
347                        ISupplicantNetwork network) -> {
348                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
349                    if (!statusSuccess.value) {
350                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
351                    } else {
352                        newNetwork.value = network;
353                    }
354                });
355            } catch (RemoteException e) {
356                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
357                supplicantServiceDiedHandler();
358            }
359            if (statusSuccess.value) {
360                return new SupplicantStaNetworkHal(ISupplicantStaNetwork.asInterface(
361                        newNetwork.value.asBinder()), mHandlerThread);
362            } else {
363                return null;
364            }
365        }
366    }
367
368    /**
369     * Remove network from supplicant with network Id
370     *
371     * @return true if request is sent successfully, false otherwise.
372     */
373    private boolean removeNetwork(int id) {
374        synchronized (mLock) {
375            final String methodStr = "removeNetwork";
376            if (DBG) Log.i(TAG, methodStr);
377            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
378            try {
379                SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
380                return checkStatusAndLogFailure(status, methodStr);
381            } catch (RemoteException e) {
382                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
383                supplicantServiceDiedHandler();
384                return false;
385            }
386        }
387    }
388
389    /**
390     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
391     * the call fails
392     */
393    private SupplicantStaNetworkHal getNetwork(int id) {
394        synchronized (mLock) {
395            MutableBoolean statusSuccess = new MutableBoolean(false);
396            Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
397            final String methodStr = "getNetwork";
398            if (DBG) Log.i(TAG, methodStr);
399            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
400            try {
401                mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
402                        ISupplicantNetwork network) -> {
403                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
404                    if (!statusSuccess.value) {
405                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
406                    } else {
407                        gotNetwork.value = network;
408                    }
409                });
410            } catch (RemoteException e) {
411                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
412                supplicantServiceDiedHandler();
413            }
414            if (statusSuccess.value) {
415                return new SupplicantStaNetworkHal(ISupplicantStaNetwork.asInterface(
416                        gotNetwork.value.asBinder()), mHandlerThread);
417            } else {
418                return null;
419            }
420        }
421    }
422
423    /**
424     * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
425     * null if the call fails
426     */
427    private java.util.ArrayList<Integer> listNetworks() {
428        synchronized (mLock) {
429            MutableBoolean statusSuccess = new MutableBoolean(false);
430            Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
431            final String methodStr = "listNetworks";
432            if (DBG) Log.i(TAG, methodStr);
433            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
434            try {
435                mISupplicantStaIface.listNetworks((SupplicantStatus status,
436                        java.util.ArrayList<Integer> networkIds) -> {
437                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
438                    if (!statusSuccess.value) {
439                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
440                    } else {
441                        networkIdList.value = networkIds;
442                    }
443                });
444            } catch (RemoteException e) {
445                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
446                supplicantServiceDiedHandler();
447            }
448            if (statusSuccess.value) {
449                return networkIdList.value;
450            } else {
451                return null;
452            }
453        }
454    }
455
456    /**
457     * Trigger a reassociation even if the iface is currently connected.
458     *
459     * @return true if request is sent successfully, false otherwise.
460     */
461    public boolean reassociate() {
462        synchronized (mLock) {
463            final String methodStr = "reassociate";
464            if (DBG) Log.i(TAG, methodStr);
465            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
466            try {
467                SupplicantStatus status = mISupplicantStaIface.reassociate();
468                return checkStatusAndLogFailure(status, methodStr);
469            } catch (RemoteException e) {
470                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
471                supplicantServiceDiedHandler();
472                return false;
473            }
474        }
475    }
476
477    /**
478     * Trigger a reconnection if the iface is disconnected.
479     *
480     * @return true if request is sent successfully, false otherwise.
481     */
482    public boolean reconnect() {
483        synchronized (mLock) {
484            final String methodStr = "reconnect";
485            if (DBG) Log.i(TAG, methodStr);
486            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
487            try {
488                SupplicantStatus status = mISupplicantStaIface.reconnect();
489                return checkStatusAndLogFailure(status, methodStr);
490            } catch (RemoteException e) {
491                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
492                supplicantServiceDiedHandler();
493                return false;
494            }
495        }
496    }
497
498    /**
499     * Trigger a disconnection from the currently connected network.
500     *
501     * @return true if request is sent successfully, false otherwise.
502     */
503    public boolean disconnect() {
504        synchronized (mLock) {
505            final String methodStr = "disconnect";
506            if (DBG) Log.i(TAG, methodStr);
507            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
508            try {
509                SupplicantStatus status = mISupplicantStaIface.disconnect();
510                return checkStatusAndLogFailure(status, methodStr);
511            } catch (RemoteException e) {
512                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
513                supplicantServiceDiedHandler();
514                return false;
515            }
516        }
517    }
518
519    /**
520     * Enable or disable power save mode.
521     *
522     * @param enable true to enable, false to disable.
523     * @return true if request is sent successfully, false otherwise.
524     */
525    public boolean setPowerSave(boolean enable) {
526        synchronized (mLock) {
527            final String methodStr = "setPowerSave";
528            if (DBG) Log.i(TAG, methodStr);
529            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
530            try {
531                SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
532                return checkStatusAndLogFailure(status, methodStr);
533            } catch (RemoteException e) {
534                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
535                supplicantServiceDiedHandler();
536                return false;
537            }
538        }
539    }
540
541    /**
542     * Initiate TDLS discover with the specified AP.
543     *
544     * @param macAddress MAC Address of the AP.
545     * @return true if request is sent successfully, false otherwise.
546     */
547    public boolean initiateTdlsDiscover(String macAddress) {
548        return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
549    }
550    /** See ISupplicantStaIface.hal for documentation */
551    private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
552        synchronized (mLock) {
553            final String methodStr = "initiateTdlsDiscover";
554            if (DBG) Log.i(TAG, methodStr);
555            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
556            try {
557                SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
558                return checkStatusAndLogFailure(status, methodStr);
559            } catch (RemoteException e) {
560                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
561                supplicantServiceDiedHandler();
562                return false;
563            }
564        }
565    }
566
567    /**
568     * Initiate TDLS setup with the specified AP.
569     *
570     * @param macAddress MAC Address of the AP.
571     * @return true if request is sent successfully, false otherwise.
572     */
573    public boolean initiateTdlsSetup(String macAddress) {
574        return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
575    }
576    /** See ISupplicantStaIface.hal for documentation */
577    private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
578        synchronized (mLock) {
579            final String methodStr = "initiateTdlsSetup";
580            if (DBG) Log.i(TAG, methodStr);
581            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
582            try {
583                SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
584                return checkStatusAndLogFailure(status, methodStr);
585            } catch (RemoteException e) {
586                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
587                supplicantServiceDiedHandler();
588                return false;
589            }
590        }
591    }
592
593    /**
594     * Initiate TDLS teardown with the specified AP.
595     * @param macAddress MAC Address of the AP.
596     * @return true if request is sent successfully, false otherwise.
597     */
598    public boolean initiateTdlsTeardown(String macAddress) {
599        return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
600    }
601
602    /** See ISupplicantStaIface.hal for documentation */
603    private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
604        synchronized (mLock) {
605            final String methodStr = "initiateTdlsTeardown";
606            if (DBG) Log.i(TAG, methodStr);
607            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
608            try {
609                SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
610                return checkStatusAndLogFailure(status, methodStr);
611            } catch (RemoteException e) {
612                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
613                supplicantServiceDiedHandler();
614                return false;
615            }
616        }
617    }
618
619    /**
620     * Request the specified ANQP elements |elements| from the specified AP |bssid|.
621     *
622     * @param bssid BSSID of the AP
623     * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
624     * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
625     * @return true if request is sent successfully, false otherwise.
626     */
627    public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
628                                     ArrayList<Integer> hs20SubTypes) {
629        return initiateAnqpQuery(
630                NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
631    }
632
633    /** See ISupplicantStaIface.hal for documentation */
634    private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
635            java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
636        synchronized (mLock) {
637            final String methodStr = "initiateAnqpQuery";
638            if (DBG) Log.i(TAG, methodStr);
639            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
640            try {
641                SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
642                        infoElements, subTypes);
643                return checkStatusAndLogFailure(status, methodStr);
644            } catch (RemoteException e) {
645                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
646                supplicantServiceDiedHandler();
647                return false;
648            }
649        }
650    }
651
652    /**
653     * Request the specified ANQP ICON from the specified AP |bssid|.
654     *
655     * @param bssid BSSID of the AP
656     * @param fileName Name of the file to request.
657     * @return true if request is sent successfully, false otherwise.
658     */
659    public boolean initiateHs20IconQuery(String bssid, String fileName) {
660        return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
661    }
662
663    /** See ISupplicantStaIface.hal for documentation */
664    private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
665        synchronized (mLock) {
666            final String methodStr = "initiateHs20IconQuery";
667            if (DBG) Log.i(TAG, methodStr);
668            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
669            try {
670                SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
671                        fileName);
672                return checkStatusAndLogFailure(status, methodStr);
673            } catch (RemoteException e) {
674                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
675                supplicantServiceDiedHandler();
676                return false;
677            }
678        }
679    }
680
681    /**
682     * Makes a callback to HIDL to getMacAddress from supplicant
683     *
684     * @return string containing the MAC address, or null on a failed call
685     */
686    public String getMacAddress() {
687        synchronized (mLock) {
688            MutableBoolean statusSuccess = new MutableBoolean(false);
689            final String methodStr = "getMacAddress";
690            if (DBG) Log.i(TAG, methodStr);
691            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
692            Mutable<String> gotMac = new Mutable<>();
693            try {
694                mISupplicantStaIface.getMacAddress((SupplicantStatus status,
695                        byte[/* 6 */] macAddr) -> {
696                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
697                    if (!statusSuccess.value) {
698                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
699                    } else {
700                        gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
701                    }
702                });
703            } catch (RemoteException e) {
704                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
705                supplicantServiceDiedHandler();
706            }
707            if (statusSuccess.value) {
708                return gotMac.value;
709            } else {
710                return null;
711            }
712        }
713    }
714
715    /**
716     * Start using the added RX filters.
717     *
718     * @return true if request is sent successfully, false otherwise.
719     */
720    public boolean startRxFilter() {
721        synchronized (mLock) {
722            final String methodStr = "startRxFilter";
723            if (DBG) Log.i(TAG, methodStr);
724            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
725            try {
726                SupplicantStatus status = mISupplicantStaIface.startRxFilter();
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     * Stop using the added RX filters.
738     *
739     * @return true if request is sent successfully, false otherwise.
740     */
741    public boolean stopRxFilter() {
742        synchronized (mLock) {
743            final String methodStr = "stopRxFilter";
744            if (DBG) Log.i(TAG, methodStr);
745            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
746            try {
747                SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
748                return checkStatusAndLogFailure(status, methodStr);
749            } catch (RemoteException e) {
750                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
751                supplicantServiceDiedHandler();
752                return false;
753            }
754        }
755    }
756
757    public static final byte RX_FILTER_TYPE_V4_MULTICAST =
758            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
759    public static final byte RX_FILTER_TYPE_V6_MULTICAST =
760            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
761    /**
762     * Add an RX filter.
763     *
764     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
765     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
766     * @return true if request is sent successfully, false otherwise.
767     */
768    private boolean addRxFilter(byte type) {
769        synchronized (mLock) {
770            final String methodStr = "addRxFilter";
771            if (DBG) Log.i(TAG, methodStr);
772            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
773            try {
774                SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
775                return checkStatusAndLogFailure(status, methodStr);
776            } catch (RemoteException e) {
777                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
778                supplicantServiceDiedHandler();
779                return false;
780            }
781        }
782    }
783
784    /**
785     * Remove an RX filter.
786     *
787     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
788     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
789     * @return true if request is sent successfully, false otherwise.
790     */
791    private boolean removeRxFilter(byte type) {
792        synchronized (mLock) {
793            final String methodStr = "removeRxFilter";
794            if (DBG) Log.i(TAG, methodStr);
795            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
796            try {
797                SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
798                return checkStatusAndLogFailure(status, methodStr);
799            } catch (RemoteException e) {
800                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
801                supplicantServiceDiedHandler();
802                return false;
803            }
804        }
805    }
806
807    public static final byte BT_COEX_MODE_ENABLED = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
808    public static final byte BT_COEX_MODE_DISABLED = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
809    public static final byte BT_COEX_MODE_SENSE = ISupplicantStaIface.BtCoexistenceMode.SENSE;
810    /**
811     * Set Bt co existense mode.
812     *
813     * @param mode one of the above {@link #BT_COEX_MODE_ENABLED}, {@link #BT_COEX_MODE_DISABLED}
814     *             or {@link #BT_COEX_MODE_SENSE} values.
815     * @return true if request is sent successfully, false otherwise.
816     */
817    public boolean setBtCoexistenceMode(byte mode) {
818        synchronized (mLock) {
819            final String methodStr = "setBtCoexistenceMode";
820            if (DBG) Log.i(TAG, methodStr);
821            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
822            try {
823                SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
824                return checkStatusAndLogFailure(status, methodStr);
825            } catch (RemoteException e) {
826                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
827                supplicantServiceDiedHandler();
828                return false;
829            }
830        }
831    }
832
833    /** Enable or disable BT coexistence mode.
834     *
835     * @param enable true to enable, false to disable.
836     * @return true if request is sent successfully, false otherwise.
837     */
838    public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
839        synchronized (mLock) {
840            final String methodStr = "setBtCoexistenceScanModeEnabled";
841            if (DBG) Log.i(TAG, methodStr);
842            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
843            try {
844                SupplicantStatus status =
845                        mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
846                return checkStatusAndLogFailure(status, methodStr);
847            } catch (RemoteException e) {
848                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
849                supplicantServiceDiedHandler();
850                return false;
851            }
852        }
853    }
854
855    /**
856     * Enable or disable suspend mode optimizations.
857     *
858     * @param enable true to enable, false otherwise.
859     */
860    public boolean setSuspendModeEnabled(boolean enable) {
861        synchronized (mLock) {
862            final String methodStr = "setSuspendModeEnabled";
863            if (DBG) Log.i(TAG, methodStr);
864            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
865            try {
866                SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
867                return checkStatusAndLogFailure(status, methodStr);
868            } catch (RemoteException e) {
869                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
870                supplicantServiceDiedHandler();
871                return false;
872            }
873        }
874    }
875
876    /**
877     * Set country code.
878     *
879     * @param codeStr 2 byte ASCII string. For ex: US, CA.
880     * @return true if request is sent successfully, false otherwise.
881     */
882    public boolean setCountryCode(String codeStr) {
883        return setCountryCode(NativeUtil.stringToByteArray(codeStr));
884    }
885
886    /** See ISupplicantStaIface.hal for documentation */
887    private boolean setCountryCode(byte[/* 2 */] code) {
888        synchronized (mLock) {
889            final String methodStr = "setCountryCode";
890            if (DBG) Log.i(TAG, methodStr);
891            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
892            try {
893                SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
894                return checkStatusAndLogFailure(status, methodStr);
895            } catch (RemoteException e) {
896                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
897                supplicantServiceDiedHandler();
898                return false;
899            }
900        }
901    }
902
903    /**
904     * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
905     */
906    private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
907        if (DBG) Log.i(TAG, methodStr);
908        if (mISupplicantStaIface == null) {
909            Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
910            return false;
911        }
912        return true;
913    }
914
915    /**
916     * Returns true if provided status code is SUCCESS, logs debug message and returns false
917     * otherwise
918     */
919    private static boolean checkStatusAndLogFailure(SupplicantStatus status,
920            final String methodStr) {
921        if (DBG) Log.i(TAG, methodStr);
922        if (status.code != SupplicantStatusCode.SUCCESS) {
923            Log.e(TAG, methodStr + " failed: " + supplicantStatusCodeToString(status.code) + ", "
924                    + status.debugMessage);
925            return false;
926        }
927        return true;
928    }
929
930    /**
931     * Converts SupplicantStatus code values to strings for debug logging
932     * TODO(b/34811152) Remove this, or make it more break resistance
933     */
934    public static String supplicantStatusCodeToString(int code) {
935        switch (code) {
936            case 0:
937                return "SUCCESS";
938            case 1:
939                return "FAILURE_UNKNOWN";
940            case 2:
941                return "FAILURE_ARGS_INVALID";
942            case 3:
943                return "FAILURE_IFACE_INVALID";
944            case 4:
945                return "FAILURE_IFACE_UNKNOWN";
946            case 5:
947                return "FAILURE_IFACE_EXISTS";
948            case 6:
949                return "FAILURE_IFACE_DISABLED";
950            case 7:
951                return "FAILURE_IFACE_NOT_DISCONNECTED";
952            case 8:
953                return "FAILURE_NETWORK_INVALID";
954            case 9:
955                return "FAILURE_NETWORK_UNKNOWN";
956            default:
957                return "??? UNKNOWN_CODE";
958        }
959    }
960
961    private static class Mutable<E> {
962        public E value;
963
964        Mutable() {
965            value = null;
966        }
967
968        Mutable(E value) {
969            this.value = value;
970        }
971    }
972
973    private void logd(String s) {
974        Log.d(TAG, s);
975    }
976
977    private void logi(String s) {
978        Log.i(TAG, s);
979    }
980
981    private void loge(String s) {
982        Log.e(TAG, s);
983    }
984}
985