SupplicantStaIfaceHal.java revision 727ba04029935d4faee3c7fce8a5a0ba6ed0a4ea
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.select()) {
269            loge("Failed to select network configuration: " + config.configKey());
270            return false;
271        }
272        return true;
273    }
274
275    /**
276     * Remove all networks from supplicant
277     * TODO(b/34454675) use ISupplicantIface.removeAllNetworks when it exists
278     */
279    public boolean removeAllNetworks() {
280        synchronized (mLock) {
281            ArrayList<Integer> networks = listNetworks();
282            if (networks == null) {
283                Log.e(TAG, "removeAllNetworks failed, got null networks");
284                return false;
285            }
286            for (int id : networks) {
287                if (!removeNetwork(id)) {
288                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
289                    return false;
290                }
291            }
292        }
293        return true;
294    }
295
296    /**
297     * Gets the interface name.
298     *
299     * @return returns the name of Iface or null if the call fails
300     */
301    private String getName() {
302        synchronized (mLock) {
303            MutableBoolean statusSuccess = new MutableBoolean(false);
304            final String methodStr = "getName";
305            if (DBG) Log.i(TAG, methodStr);
306            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
307            final StringBuilder builder = new StringBuilder();
308            try {
309                mISupplicantStaIface.getName((SupplicantStatus status, String name) -> {
310                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
311                    if (!statusSuccess.value) {
312                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
313                    } else {
314                        builder.append(name);
315                    }
316                });
317            } catch (RemoteException e) {
318                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
319                supplicantServiceDiedHandler();
320            }
321            if (statusSuccess.value) {
322                return builder.toString();
323            } else {
324                return null;
325            }
326        }
327    }
328
329    /**
330     * Adds a new network.
331     *
332     * @return The ISupplicantNetwork object for the new network, or null if the call fails
333     */
334    private SupplicantStaNetworkHal addNetwork() {
335        synchronized (mLock) {
336            MutableBoolean statusSuccess = new MutableBoolean(false);
337            Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
338            final String methodStr = "addNetwork";
339            if (DBG) Log.i(TAG, methodStr);
340            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
341            try {
342                mISupplicantStaIface.addNetwork((SupplicantStatus status,
343                        ISupplicantNetwork network) -> {
344                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
345                    if (!statusSuccess.value) {
346                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
347                    } else {
348                        newNetwork.value = network;
349                    }
350                });
351            } catch (RemoteException e) {
352                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
353                supplicantServiceDiedHandler();
354            }
355            if (statusSuccess.value) {
356                return new SupplicantStaNetworkHal(ISupplicantStaNetwork.asInterface(
357                        newNetwork.value.asBinder()), mHandlerThread);
358            } else {
359                return null;
360            }
361        }
362    }
363
364    /**
365     * Remove network from supplicant with network Id
366     *
367     * @return true if request is sent successfully, false otherwise.
368     */
369    private boolean removeNetwork(int id) {
370        synchronized (mLock) {
371            final String methodStr = "removeNetwork";
372            if (DBG) Log.i(TAG, methodStr);
373            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
374            try {
375                SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
376                return checkStatusAndLogFailure(status, methodStr);
377            } catch (RemoteException e) {
378                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
379                supplicantServiceDiedHandler();
380                return false;
381            }
382        }
383    }
384
385    /**
386     * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
387     * the call fails
388     */
389    private SupplicantStaNetworkHal getNetwork(int id) {
390        synchronized (mLock) {
391            MutableBoolean statusSuccess = new MutableBoolean(false);
392            Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
393            final String methodStr = "getNetwork";
394            if (DBG) Log.i(TAG, methodStr);
395            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
396            try {
397                mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
398                        ISupplicantNetwork network) -> {
399                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
400                    if (!statusSuccess.value) {
401                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
402                    } else {
403                        gotNetwork.value = network;
404                    }
405                });
406            } catch (RemoteException e) {
407                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
408                supplicantServiceDiedHandler();
409            }
410            if (statusSuccess.value) {
411                return new SupplicantStaNetworkHal(ISupplicantStaNetwork.asInterface(
412                        gotNetwork.value.asBinder()), mHandlerThread);
413            } else {
414                return null;
415            }
416        }
417    }
418
419    /**
420     * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
421     * null if the call fails
422     */
423    private java.util.ArrayList<Integer> listNetworks() {
424        synchronized (mLock) {
425            MutableBoolean statusSuccess = new MutableBoolean(false);
426            Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
427            final String methodStr = "listNetworks";
428            if (DBG) Log.i(TAG, methodStr);
429            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
430            try {
431                mISupplicantStaIface.listNetworks((SupplicantStatus status,
432                        java.util.ArrayList<Integer> networkIds) -> {
433                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
434                    if (!statusSuccess.value) {
435                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
436                    } else {
437                        networkIdList.value = networkIds;
438                    }
439                });
440            } catch (RemoteException e) {
441                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
442                supplicantServiceDiedHandler();
443            }
444            if (statusSuccess.value) {
445                return networkIdList.value;
446            } else {
447                return null;
448            }
449        }
450    }
451
452    /**
453     * Trigger a reassociation even if the iface is currently connected.
454     *
455     * @return true if request is sent successfully, false otherwise.
456     */
457    public boolean reassociate() {
458        synchronized (mLock) {
459            final String methodStr = "reassociate";
460            if (DBG) Log.i(TAG, methodStr);
461            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
462            try {
463                SupplicantStatus status = mISupplicantStaIface.reassociate();
464                return checkStatusAndLogFailure(status, methodStr);
465            } catch (RemoteException e) {
466                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
467                supplicantServiceDiedHandler();
468                return false;
469            }
470        }
471    }
472
473    /**
474     * Trigger a reconnection if the iface is disconnected.
475     *
476     * @return true if request is sent successfully, false otherwise.
477     */
478    public boolean reconnect() {
479        synchronized (mLock) {
480            final String methodStr = "reconnect";
481            if (DBG) Log.i(TAG, methodStr);
482            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
483            try {
484                SupplicantStatus status = mISupplicantStaIface.reconnect();
485                return checkStatusAndLogFailure(status, methodStr);
486            } catch (RemoteException e) {
487                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
488                supplicantServiceDiedHandler();
489                return false;
490            }
491        }
492    }
493
494    /**
495     * Trigger a disconnection from the currently connected network.
496     *
497     * @return true if request is sent successfully, false otherwise.
498     */
499    public boolean disconnect() {
500        synchronized (mLock) {
501            final String methodStr = "disconnect";
502            if (DBG) Log.i(TAG, methodStr);
503            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
504            try {
505                SupplicantStatus status = mISupplicantStaIface.disconnect();
506                return checkStatusAndLogFailure(status, methodStr);
507            } catch (RemoteException e) {
508                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
509                supplicantServiceDiedHandler();
510                return false;
511            }
512        }
513    }
514
515    /**
516     * Enable or disable power save mode.
517     *
518     * @param enable true to enable, false to disable.
519     * @return true if request is sent successfully, false otherwise.
520     */
521    public boolean setPowerSave(boolean enable) {
522        synchronized (mLock) {
523            final String methodStr = "setPowerSave";
524            if (DBG) Log.i(TAG, methodStr);
525            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
526            try {
527                SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
528                return checkStatusAndLogFailure(status, methodStr);
529            } catch (RemoteException e) {
530                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
531                supplicantServiceDiedHandler();
532                return false;
533            }
534        }
535    }
536
537    /**
538     * Initiate TDLS discover with the specified AP.
539     *
540     * @param macAddress MAC Address of the AP.
541     * @return true if request is sent successfully, false otherwise.
542     */
543    public boolean initiateTdlsDiscover(String macAddress) {
544        return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
545    }
546    /** See ISupplicantStaIface.hal for documentation */
547    private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
548        synchronized (mLock) {
549            final String methodStr = "initiateTdlsDiscover";
550            if (DBG) Log.i(TAG, methodStr);
551            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
552            try {
553                SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
554                return checkStatusAndLogFailure(status, methodStr);
555            } catch (RemoteException e) {
556                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
557                supplicantServiceDiedHandler();
558                return false;
559            }
560        }
561    }
562
563    /**
564     * Initiate TDLS setup with the specified AP.
565     *
566     * @param macAddress MAC Address of the AP.
567     * @return true if request is sent successfully, false otherwise.
568     */
569    public boolean initiateTdlsSetup(String macAddress) {
570        return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
571    }
572    /** See ISupplicantStaIface.hal for documentation */
573    private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
574        synchronized (mLock) {
575            final String methodStr = "initiateTdlsSetup";
576            if (DBG) Log.i(TAG, methodStr);
577            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
578            try {
579                SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
580                return checkStatusAndLogFailure(status, methodStr);
581            } catch (RemoteException e) {
582                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
583                supplicantServiceDiedHandler();
584                return false;
585            }
586        }
587    }
588
589    /**
590     * Initiate TDLS teardown with the specified AP.
591     * @param macAddress MAC Address of the AP.
592     * @return true if request is sent successfully, false otherwise.
593     */
594    public boolean initiateTdlsTeardown(String macAddress) {
595        return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
596    }
597
598    /** See ISupplicantStaIface.hal for documentation */
599    private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
600        synchronized (mLock) {
601            final String methodStr = "initiateTdlsTeardown";
602            if (DBG) Log.i(TAG, methodStr);
603            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
604            try {
605                SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
606                return checkStatusAndLogFailure(status, methodStr);
607            } catch (RemoteException e) {
608                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
609                supplicantServiceDiedHandler();
610                return false;
611            }
612        }
613    }
614
615    /**
616     * Request the specified ANQP elements |elements| from the specified AP |bssid|.
617     *
618     * @param bssid BSSID of the AP
619     * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
620     * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
621     * @return true if request is sent successfully, false otherwise.
622     */
623    public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
624                                     ArrayList<Integer> hs20SubTypes) {
625        return initiateAnqpQuery(
626                NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
627    }
628
629    /** See ISupplicantStaIface.hal for documentation */
630    private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
631            java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
632        synchronized (mLock) {
633            final String methodStr = "initiateAnqpQuery";
634            if (DBG) Log.i(TAG, methodStr);
635            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
636            try {
637                SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
638                        infoElements, subTypes);
639                return checkStatusAndLogFailure(status, methodStr);
640            } catch (RemoteException e) {
641                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
642                supplicantServiceDiedHandler();
643                return false;
644            }
645        }
646    }
647
648    /**
649     * Request the specified ANQP ICON from the specified AP |bssid|.
650     *
651     * @param bssid BSSID of the AP
652     * @param fileName Name of the file to request.
653     * @return true if request is sent successfully, false otherwise.
654     */
655    public boolean initiateHs20IconQuery(String bssid, String fileName) {
656        return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
657    }
658
659    /** See ISupplicantStaIface.hal for documentation */
660    private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
661        synchronized (mLock) {
662            final String methodStr = "initiateHs20IconQuery";
663            if (DBG) Log.i(TAG, methodStr);
664            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
665            try {
666                SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
667                        fileName);
668                return checkStatusAndLogFailure(status, methodStr);
669            } catch (RemoteException e) {
670                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
671                supplicantServiceDiedHandler();
672                return false;
673            }
674        }
675    }
676
677    /**
678     * Makes a callback to HIDL to getMacAddress from supplicant
679     *
680     * @return string containing the MAC address, or null on a failed call
681     */
682    public String getMacAddress() {
683        synchronized (mLock) {
684            MutableBoolean statusSuccess = new MutableBoolean(false);
685            final String methodStr = "getMacAddress";
686            if (DBG) Log.i(TAG, methodStr);
687            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
688            Mutable<String> gotMac = new Mutable<>();
689            try {
690                mISupplicantStaIface.getMacAddress((SupplicantStatus status,
691                        byte[/* 6 */] macAddr) -> {
692                    statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS;
693                    if (!statusSuccess.value) {
694                        Log.e(TAG, methodStr + " failed: " + status.debugMessage);
695                    } else {
696                        gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
697                    }
698                });
699            } catch (RemoteException e) {
700                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e);
701                supplicantServiceDiedHandler();
702            }
703            if (statusSuccess.value) {
704                return gotMac.value;
705            } else {
706                return null;
707            }
708        }
709    }
710
711    /**
712     * Start using the added RX filters.
713     *
714     * @return true if request is sent successfully, false otherwise.
715     */
716    public boolean startRxFilter() {
717        synchronized (mLock) {
718            final String methodStr = "startRxFilter";
719            if (DBG) Log.i(TAG, methodStr);
720            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
721            try {
722                SupplicantStatus status = mISupplicantStaIface.startRxFilter();
723                return checkStatusAndLogFailure(status, methodStr);
724            } catch (RemoteException e) {
725                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
726                supplicantServiceDiedHandler();
727                return false;
728            }
729        }
730    }
731
732    /**
733     * Stop using the added RX filters.
734     *
735     * @return true if request is sent successfully, false otherwise.
736     */
737    public boolean stopRxFilter() {
738        synchronized (mLock) {
739            final String methodStr = "stopRxFilter";
740            if (DBG) Log.i(TAG, methodStr);
741            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
742            try {
743                SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
744                return checkStatusAndLogFailure(status, methodStr);
745            } catch (RemoteException e) {
746                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
747                supplicantServiceDiedHandler();
748                return false;
749            }
750        }
751    }
752
753    public static final byte RX_FILTER_TYPE_V4_MULTICAST =
754            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
755    public static final byte RX_FILTER_TYPE_V6_MULTICAST =
756            ISupplicantStaIface.RxFilterType.V6_MULTICAST;
757    /**
758     * Add an RX filter.
759     *
760     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
761     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
762     * @return true if request is sent successfully, false otherwise.
763     */
764    private boolean addRxFilter(byte type) {
765        synchronized (mLock) {
766            final String methodStr = "addRxFilter";
767            if (DBG) Log.i(TAG, methodStr);
768            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
769            try {
770                SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
771                return checkStatusAndLogFailure(status, methodStr);
772            } catch (RemoteException e) {
773                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
774                supplicantServiceDiedHandler();
775                return false;
776            }
777        }
778    }
779
780    /**
781     * Remove an RX filter.
782     *
783     * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or
784     *        {@link #RX_FILTER_TYPE_V6_MULTICAST} values.
785     * @return true if request is sent successfully, false otherwise.
786     */
787    private boolean removeRxFilter(byte type) {
788        synchronized (mLock) {
789            final String methodStr = "removeRxFilter";
790            if (DBG) Log.i(TAG, methodStr);
791            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
792            try {
793                SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
794                return checkStatusAndLogFailure(status, methodStr);
795            } catch (RemoteException e) {
796                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
797                supplicantServiceDiedHandler();
798                return false;
799            }
800        }
801    }
802
803    public static final byte BT_COEX_MODE_ENABLED = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
804    public static final byte BT_COEX_MODE_DISABLED = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
805    public static final byte BT_COEX_MODE_SENSE = ISupplicantStaIface.BtCoexistenceMode.SENSE;
806    /**
807     * Set Bt co existense mode.
808     *
809     * @param mode one of the above {@link #BT_COEX_MODE_ENABLED}, {@link #BT_COEX_MODE_DISABLED}
810     *             or {@link #BT_COEX_MODE_SENSE} values.
811     * @return true if request is sent successfully, false otherwise.
812     */
813    public boolean setBtCoexistenceMode(byte mode) {
814        synchronized (mLock) {
815            final String methodStr = "setBtCoexistenceMode";
816            if (DBG) Log.i(TAG, methodStr);
817            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
818            try {
819                SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
820                return checkStatusAndLogFailure(status, methodStr);
821            } catch (RemoteException e) {
822                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
823                supplicantServiceDiedHandler();
824                return false;
825            }
826        }
827    }
828
829    /** Enable or disable BT coexistence mode.
830     *
831     * @param enable true to enable, false to disable.
832     * @return true if request is sent successfully, false otherwise.
833     */
834    public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
835        synchronized (mLock) {
836            final String methodStr = "setBtCoexistenceScanModeEnabled";
837            if (DBG) Log.i(TAG, methodStr);
838            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
839            try {
840                SupplicantStatus status =
841                        mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
842                return checkStatusAndLogFailure(status, methodStr);
843            } catch (RemoteException e) {
844                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
845                supplicantServiceDiedHandler();
846                return false;
847            }
848        }
849    }
850
851    /**
852     * Enable or disable suspend mode optimizations.
853     *
854     * @param enable true to enable, false otherwise.
855     */
856    public boolean setSuspendModeEnabled(boolean enable) {
857        synchronized (mLock) {
858            final String methodStr = "setSuspendModeEnabled";
859            if (DBG) Log.i(TAG, methodStr);
860            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
861            try {
862                SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
863                return checkStatusAndLogFailure(status, methodStr);
864            } catch (RemoteException e) {
865                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
866                supplicantServiceDiedHandler();
867                return false;
868            }
869        }
870    }
871
872    /**
873     * Set country code.
874     *
875     * @param codeStr 2 byte ASCII string. For ex: US, CA.
876     * @return true if request is sent successfully, false otherwise.
877     */
878    public boolean setCountryCode(String codeStr) {
879        return setCountryCode(NativeUtil.stringToByteArray(codeStr));
880    }
881
882    /** See ISupplicantStaIface.hal for documentation */
883    private boolean setCountryCode(byte[/* 2 */] code) {
884        synchronized (mLock) {
885            final String methodStr = "setCountryCode";
886            if (DBG) Log.i(TAG, methodStr);
887            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
888            try {
889                SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
890                return checkStatusAndLogFailure(status, methodStr);
891            } catch (RemoteException e) {
892                Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e);
893                supplicantServiceDiedHandler();
894                return false;
895            }
896        }
897    }
898
899    /**
900     * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
901     */
902    private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
903        if (DBG) Log.i(TAG, methodStr);
904        if (mISupplicantStaIface == null) {
905            Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
906            return false;
907        }
908        return true;
909    }
910
911    /**
912     * Returns true if provided status code is SUCCESS, logs debug message and returns false
913     * otherwise
914     */
915    private static boolean checkStatusAndLogFailure(SupplicantStatus status,
916            final String methodStr) {
917        if (DBG) Log.i(TAG, methodStr);
918        if (status.code != SupplicantStatusCode.SUCCESS) {
919            Log.e(TAG, methodStr + " failed: " + supplicantStatusCodeToString(status.code) + ", "
920                    + status.debugMessage);
921            return false;
922        }
923        return true;
924    }
925
926    /**
927     * Converts SupplicantStatus code values to strings for debug logging
928     * TODO(b/34811152) Remove this, or make it more break resistance
929     */
930    public static String supplicantStatusCodeToString(int code) {
931        switch (code) {
932            case 0:
933                return "SUCCESS";
934            case 1:
935                return "FAILURE_UNKNOWN";
936            case 2:
937                return "FAILURE_ARGS_INVALID";
938            case 3:
939                return "FAILURE_IFACE_INVALID";
940            case 4:
941                return "FAILURE_IFACE_UNKNOWN";
942            case 5:
943                return "FAILURE_IFACE_EXISTS";
944            case 6:
945                return "FAILURE_IFACE_DISABLED";
946            case 7:
947                return "FAILURE_IFACE_NOT_DISCONNECTED";
948            case 8:
949                return "FAILURE_NETWORK_INVALID";
950            case 9:
951                return "FAILURE_NETWORK_UNKNOWN";
952            default:
953                return "??? UNKNOWN_CODE";
954        }
955    }
956
957    private static class Mutable<E> {
958        public E value;
959
960        Mutable() {
961            value = null;
962        }
963
964        Mutable(E value) {
965            this.value = value;
966        }
967    }
968
969    private void logd(String s) {
970        Log.d(TAG, s);
971    }
972
973    private void logi(String s) {
974        Log.i(TAG, s);
975    }
976
977    private void loge(String s) {
978        Log.e(TAG, s);
979    }
980}
981