WifiVendorHal.java revision 4f2049a015722cae0f0169379d499d5d4fc98e30
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.annotation.Nullable;
19import android.hardware.wifi.V1_0.IWifiApIface;
20import android.hardware.wifi.V1_0.IWifiChip;
21import android.hardware.wifi.V1_0.IWifiIface;
22import android.hardware.wifi.V1_0.IWifiRttController;
23import android.hardware.wifi.V1_0.IWifiStaIface;
24import android.hardware.wifi.V1_0.StaRoamingConfig;
25import android.hardware.wifi.V1_0.StaRoamingState;
26import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
27import android.hardware.wifi.V1_0.WifiStatus;
28import android.hardware.wifi.V1_0.WifiStatusCode;
29import android.net.apf.ApfCapabilities;
30import android.net.wifi.RttManager;
31import android.net.wifi.RttManager.ResponderConfig;
32import android.net.wifi.WifiInfo;
33import android.net.wifi.WifiLinkLayerStats;
34import android.net.wifi.WifiScanner;
35import android.net.wifi.WifiWakeReasonAndCounts;
36import android.os.HandlerThread;
37import android.os.RemoteException;
38import android.util.Log;
39import android.util.MutableBoolean;
40
41import com.android.internal.annotations.VisibleForTesting;
42import com.android.server.connectivity.KeepalivePacketData;
43
44/**
45 * Vendor HAL via HIDL
46 */
47public class WifiVendorHal {
48
49    private static final String TAG = "WifiVendorHal";
50
51    // Vendor HAL HIDL interface objects.
52    private IWifiChip mIWifiChip;
53    private IWifiStaIface mIWifiStaIface;
54    private IWifiApIface mIWifiApIface;
55    private IWifiRttController mIWifiRttController;
56    private final HalDeviceManager mHalDeviceManager;
57    private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
58    private final HandlerThread mWifiStateMachineHandlerThread;
59
60    public WifiVendorHal(HalDeviceManager halDeviceManager,
61                         HandlerThread wifiStateMachineHandlerThread) {
62        mHalDeviceManager = halDeviceManager;
63        mWifiStateMachineHandlerThread = wifiStateMachineHandlerThread;
64        mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener();
65    }
66
67    // TODO(mplass): figure out where we need locking in hidl world. b/33383725
68    public static final Object sLock = new Object();
69
70    private void handleRemoteException(RemoteException e) {
71        kilroy();
72        Log.e(TAG, "RemoteException in HIDL call " + e);
73    }
74
75    private void noteHidlError(WifiStatus status, String culprit) {
76        kilroy();
77        Log.e(TAG, "Error in " + culprit + " code: " + status.code
78                + " (" + status.description + ")");
79    }
80
81    /**
82     * Initialize the Hal device manager and register for status callbacks.
83     * @return
84     */
85    public boolean initialize() {
86        mHalDeviceManager.initialize();
87        mHalDeviceManager.registerStatusListener(
88                mHalDeviceManagerStatusCallbacks, mWifiStateMachineHandlerThread.getLooper());
89        return true;
90    }
91
92    /**
93     * Bring up the HIDL Vendor HAL and configure for AP (Access Point) mode
94     * @return true for success
95     */
96    public boolean startVendorHalAp() {
97        return startVendorHal(AP_MODE);
98    }
99
100     /**
101     * Bring up the HIDL Vendor HAL and configure for STA (Station) mode
102     * @return true for success
103     */
104    public boolean startVendorHalSta() {
105        return startVendorHal(STA_MODE);
106    }
107
108
109    public static final boolean STA_MODE = true;
110    public static final boolean AP_MODE = false;
111
112    /**
113     * Bring up the HIDL Vendor HAL and configure for STA mode or AP mode.
114     *
115     * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
116     */
117    public boolean startVendorHal(boolean isStaMode) {
118        if (!mHalDeviceManager.start()) {
119            Log.e(TAG, "Failed to start the vendor HAL");
120            return false;
121        }
122        IWifiIface iface;
123        if (isStaMode) {
124            mIWifiStaIface = mHalDeviceManager.createStaIface(null, null);
125            if (mIWifiStaIface == null) {
126                Log.e(TAG, "Failed to create STA Iface. Vendor Hal start failed");
127                mHalDeviceManager.stop();
128                return false;
129            }
130            iface = (IWifiIface) mIWifiStaIface;
131            mIWifiRttController = mHalDeviceManager.createRttController(iface);
132            if (mIWifiRttController == null) {
133                Log.e(TAG, "Failed to create RTT controller. Vendor Hal start failed");
134                stopVendorHal();
135                return false;
136            }
137            enableLinkLayerStats();
138        } else {
139            mIWifiApIface = mHalDeviceManager.createApIface(null, null);
140            if (mIWifiApIface == null) {
141                Log.e(TAG, "Failed to create AP Iface. Vendor Hal start failed");
142                stopVendorHal();
143                return false;
144            }
145            iface = (IWifiIface) mIWifiApIface;
146        }
147        mIWifiChip = mHalDeviceManager.getChip(iface);
148        if (mIWifiChip == null) {
149            Log.e(TAG, "Failed to get the chip created for the Iface. Vendor Hal start failed");
150            stopVendorHal();
151            return false;
152        }
153        Log.i(TAG, "Vendor Hal started successfully");
154        return true;
155    }
156
157    /**
158     * Stops the HAL
159     */
160    public void stopVendorHal() {
161        mHalDeviceManager.stop();
162        Log.i(TAG, "Vendor Hal stopped");
163    }
164
165    /**
166     * Tests whether the HAL is running or not
167     */
168    public boolean isHalStarted() {
169        return (mIWifiStaIface != null || mIWifiApIface != null);
170    }
171
172    /**
173     * Gets the scan capabilities
174     *
175     * @param capabilities object to be filled in
176     * @return true for success. false for failure
177     */
178    public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) {
179        kilroy();
180        throw new UnsupportedOperationException();
181    }
182
183    /**
184     * to be implemented
185     */
186    public boolean startScan(WifiNative.ScanSettings settings,
187                             WifiNative.ScanEventHandler eventHandler) {
188        kilroy();
189        throw new UnsupportedOperationException();
190    }
191
192    /**
193     * to be implemented
194     */
195    public void stopScan() {
196        kilroy();
197        throw new UnsupportedOperationException();
198    }
199
200    /**
201     * to be implemented
202     */
203    public void pauseScan() {
204        kilroy();
205        throw new UnsupportedOperationException();
206    }
207
208    /**
209     * to be implemented
210     */
211    public void restartScan() {
212        kilroy();
213        throw new UnsupportedOperationException();
214    }
215
216    /**
217     * to be implemented
218     */
219    public WifiScanner.ScanData[] getScanResults(boolean flush) {
220        kilroy();
221        throw new UnsupportedOperationException();
222    }
223
224    /**
225     * Get the link layer statistics
226     *
227     * Note - we always enable link layer stats on a STA interface.
228     *
229     * @return the statistics, or null if unable to do so
230     */
231    public WifiLinkLayerStats getWifiLinkLayerStats() {
232        kilroy();
233        synchronized (sLock) {
234            try {
235                if (mIWifiStaIface == null) return null;
236                kilroy();
237                WifiLinkLayerStats out = new WifiLinkLayerStats();
238                MutableBoolean ok = new MutableBoolean(false);
239                kilroy();
240                mIWifiStaIface.getLinkLayerStats((status, stats) -> {
241                            kilroy();
242                            if (status.code != WifiStatusCode.SUCCESS) return;
243                            out.status = 0; // TODO
244                            out.SSID = null; // TODO
245                            out.BSSID = null; // TODO
246                            out.beacon_rx = stats.iface.beaconRx;
247                            out.rssi_mgmt = stats.iface.avgRssiMgmt;
248                        /* WME Best Effort Access Category */
249                            out.rxmpdu_be = stats.iface.wmeBePktStats.rxMpdu;
250                            out.txmpdu_be = stats.iface.wmeBePktStats.txMpdu;
251                            out.lostmpdu_be = stats.iface.wmeBePktStats.lostMpdu;
252                            out.retries_be = stats.iface.wmeBePktStats.retries;
253                        /* WME Background Access Category */
254                            out.rxmpdu_bk = stats.iface.wmeBkPktStats.rxMpdu;
255                            out.txmpdu_bk = stats.iface.wmeBkPktStats.txMpdu;
256                            out.lostmpdu_bk = stats.iface.wmeBkPktStats.lostMpdu;
257                            out.retries_bk = stats.iface.wmeBkPktStats.retries;
258                        /* WME Video Access Category */
259                            out.rxmpdu_vi = stats.iface.wmeViPktStats.rxMpdu;
260                            out.txmpdu_vi = stats.iface.wmeViPktStats.txMpdu;
261                            out.lostmpdu_vi = stats.iface.wmeViPktStats.lostMpdu;
262                            out.retries_vi = stats.iface.wmeViPktStats.retries;
263                        /* WME Voice Access Category */
264                            out.rxmpdu_vo = stats.iface.wmeVoPktStats.rxMpdu;
265                            out.txmpdu_vo = stats.iface.wmeVoPktStats.txMpdu;
266                            out.lostmpdu_vo = stats.iface.wmeVoPktStats.lostMpdu;
267                            out.retries_vo = stats.iface.wmeVoPktStats.retries;
268                            out.on_time = stats.radio.onTimeInMs;
269                            out.tx_time = stats.radio.txTimeInMs;
270                            out.tx_time_per_level = new int[stats.radio.txTimeInMsPerLevel.size()];
271                            for (int i = 0; i < out.tx_time_per_level.length; i++) {
272                                out.tx_time_per_level[i] = stats.radio.txTimeInMsPerLevel.get(i);
273                            }
274                            out.rx_time = stats.radio.rxTimeInMs;
275                            out.on_time_scan = stats.radio.onTimeInMsForScan;
276                            kilroy();
277                            ok.value = true;
278                        }
279                );
280                return ok.value ? out : null;
281            } catch (RemoteException e) {
282                kilroy();
283                handleRemoteException(e);
284                return null;
285            }
286        }
287    }
288
289    @VisibleForTesting
290    boolean mLinkLayerStatsDebug = false;  // Passed to Hal
291
292    /**
293     * Enables the linkLayerStats in the Hal.
294     *
295     * This is called unconditionally whenever we create a STA interface.
296     *
297     */
298    private void enableLinkLayerStats() {
299        synchronized (sLock) {
300            try {
301                kilroy();
302                WifiStatus status;
303                status = mIWifiStaIface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
304                if (status.code != WifiStatusCode.SUCCESS) {
305                    kilroy();
306                    Log.e(TAG, "unable to enable link layer stats collection");
307                }
308            } catch (RemoteException e) {
309                kilroy();
310                handleRemoteException(e);
311            }
312        }
313    }
314
315    /**
316     * Get the supported features
317     *
318     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
319     */
320    public int getSupportedFeatureSet() {
321        kilroy();
322        throw new UnsupportedOperationException();
323    }
324
325    /* RTT related commands/events */
326
327    /**
328     * Starts a new rtt request
329     *
330     * @param params
331     * @param handler
332     * @return success indication
333     */
334    public boolean requestRtt(RttManager.RttParams[] params, WifiNative.RttEventHandler handler) {
335        kilroy();
336        throw new UnsupportedOperationException();
337    }
338
339    /**
340     * Cancels an outstanding rtt request
341     *
342     * @param params
343     * @return true if there was an outstanding request and it was successfully cancelled
344     */
345    public boolean cancelRtt(RttManager.RttParams[] params) {
346        kilroy();
347        throw new UnsupportedOperationException();
348    }
349
350    /**
351     * Enables RTT responder role on the device.
352     *
353     * @return {@link ResponderConfig} if the responder role is successfully enabled,
354     * {@code null} otherwise.
355     */
356    @Nullable
357    public ResponderConfig enableRttResponder(int timeoutSeconds) {
358        kilroy();
359        throw new UnsupportedOperationException();
360    }
361
362    /**
363     * Disables RTT responder role.
364     *
365     * @return {@code true} if responder role is successfully disabled,
366     * {@code false} otherwise.
367     */
368    public boolean disableRttResponder() {
369        kilroy();
370        throw new UnsupportedOperationException();
371    }
372
373    /**
374     * not supported
375     */
376    public boolean setScanningMacOui(byte[] oui) {
377        kilroy();
378        throw new UnsupportedOperationException();
379    }
380
381    /**
382     * not supported
383     */
384    public int[] getChannelsForBand(int band) {
385        kilroy();
386        throw new UnsupportedOperationException();
387    }
388
389    /**
390     * not supported
391     */
392    public boolean isGetChannelsForBandSupported() {
393        kilroy();
394        throw new UnsupportedOperationException();
395    }
396
397    /**
398     * Set DFS - actually, this is always on.
399     *
400     * @param dfsOn
401     * @return success indication
402     */
403    public boolean setDfsFlag(boolean dfsOn) {
404        kilroy();
405        throw new UnsupportedOperationException();
406    }
407
408    /**
409     * RTT (Round Trip Time) measurement capabilities of the device.
410     */
411    public RttManager.RttCapabilities getRttCapabilities() {
412        kilroy();
413        throw new UnsupportedOperationException();
414    }
415
416    /**
417     * Get the APF (Android Packet Filter) capabilities of the device
418     */
419    public ApfCapabilities getApfCapabilities() {
420        kilroy();
421        throw new UnsupportedOperationException();
422    }
423
424    private static final ApfCapabilities sNoApfCapabilities = new ApfCapabilities(0, 0, 0);
425
426    /**
427     * Installs an APF program on this iface, replacing an existing
428     * program if present.
429     */
430    public boolean installPacketFilter(byte[] filter) {
431        kilroy();
432        throw new UnsupportedOperationException();
433    }
434
435
436    /**
437     * to be implemented
438     */
439    public boolean setCountryCodeHal(String countryCode) {
440        kilroy();
441        throw new UnsupportedOperationException();
442    }
443
444    /**
445     * not to be implemented
446     */
447    public boolean enableDisableTdls(boolean enable, String macAdd,
448                                     WifiNative.TdlsEventHandler tdlsCallBack) {
449        kilroy();
450        throw new UnsupportedOperationException();
451    }
452
453    /**
454     * not to be implemented
455     */
456    public WifiNative.TdlsStatus getTdlsStatus(String macAdd) {
457        kilroy();
458        throw new UnsupportedOperationException();
459    }
460
461    /**
462     * not to be implemented
463     */
464    public WifiNative.TdlsCapabilities getTdlsCapabilities() {
465        kilroy();
466        throw new UnsupportedOperationException();
467    }
468
469    /**
470     * to be implemented
471     */
472    public boolean setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler) {
473        kilroy();
474        throw new UnsupportedOperationException();
475    }
476
477    /**
478     * Control debug data collection
479     *
480     * @param verboseLevel       0 to 3, inclusive. 0 stops logging.
481     * @param flags              Ignored.
482     * @param maxIntervalInSec   Maximum interval between reports; ignore if 0.
483     * @param minDataSizeInBytes Minimum data size in buffer for report; ignore if 0.
484     * @param ringName           Name of the ring for which data collection is to start.
485     * @return true for success
486     */
487    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
488                                          int minDataSizeInBytes, String ringName) {
489        kilroy();
490        throw new UnsupportedOperationException();
491    }
492
493    /**
494     * Pointlessly fail
495     *
496     * @return -1
497     */
498    public int getSupportedLoggerFeatureSet() {
499        return -1;
500    }
501
502    /**
503     * to be implemented
504     */
505    public boolean resetLogHandler() {
506        kilroy();
507        throw new UnsupportedOperationException();
508    }
509
510    private String mDriverDescription;
511
512    /**
513     * Vendor-provided wifi driver version string
514     */
515    public String getDriverVersion() {
516        kilroy();
517        throw new UnsupportedOperationException();
518    }
519
520    private String mFirmwareDescription;
521
522    /**
523     * Vendor-provided wifi firmware version string
524     */
525    public String getFirmwareVersion() {
526        kilroy();
527        throw new UnsupportedOperationException();
528    }
529
530    /**
531     * API to get the status of all ring buffers supported by driver
532     */
533    public WifiNative.RingBufferStatus[] getRingBufferStatus() {
534        kilroy();
535        throw new UnsupportedOperationException();
536    }
537
538    /**
539     * indicates to driver that all
540     * the data has to be uploaded urgently
541     */
542    public boolean getRingBufferData(String ringName) {
543        kilroy();
544        throw new UnsupportedOperationException();
545    }
546
547    /**
548     * to be implemented via mIWifiChip.requestFirmwareDebugDump
549     */
550    public byte[] getFwMemoryDump() {
551        kilroy();
552        throw new UnsupportedOperationException();
553    }
554
555    /**
556     * Request vendor debug info from the driver
557     */
558    public byte[] getDriverStateDump() {
559        kilroy();
560        throw new UnsupportedOperationException();
561    }
562
563    /**
564     * Start packet fate monitoring
565     * <p>
566     * Once started, monitoring remains active until HAL is unloaded.
567     *
568     * @return true for success
569     */
570    public boolean startPktFateMonitoring() {
571        kilroy();
572        throw new UnsupportedOperationException();
573    }
574
575    /**
576     * Retrieve fates of outbound packets
577     * <p>
578     * Reports the outbound frames for the most recent association (space allowing).
579     *
580     * @param reportBufs
581     * @return true for success
582     */
583    public boolean getTxPktFates(WifiNative.TxFateReport[] reportBufs) {
584        kilroy();
585        throw new UnsupportedOperationException();
586    }
587
588    /**
589     * Retrieve fates of inbound packets
590     * <p>
591     * Reports the inbound frames for the most recent association (space allowing).
592     *
593     * @param reportBufs
594     * @return true for success
595     */
596    public boolean getRxPktFates(WifiNative.RxFateReport[] reportBufs) {
597        kilroy();
598        throw new UnsupportedOperationException();
599    }
600
601    /**
602     * Start sending the specified keep alive packets periodically.
603     *
604     * @return 0 for success, -1 for error
605     */
606    public int startSendingOffloadedPacket(
607            int slot, KeepalivePacketData keepAlivePacket, int periodInMs) {
608        kilroy();
609        throw new UnsupportedOperationException();
610    }
611
612    /**
613     * Stop sending the specified keep alive packets.
614     *
615     * @param slot id - same as startSendingOffloadedPacket call.
616     * @return 0 for success, -1 for error
617     */
618    public int stopSendingOffloadedPacket(int slot) {
619        kilroy();
620        throw new UnsupportedOperationException();
621    }
622
623    /**
624     * Start RSSI monitoring on the currently connected access point.
625     *
626     * @param maxRssi          Maximum RSSI threshold.
627     * @param minRssi          Minimum RSSI threshold.
628     * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
629     * @return 0 for success, -1 for failure
630     */
631    public int startRssiMonitoring(byte maxRssi, byte minRssi,
632                                   WifiNative.WifiRssiEventHandler rssiEventHandler) {
633        kilroy();
634        throw new UnsupportedOperationException();
635    }
636
637    /**
638     * Stop RSSI monitoring
639     *
640     * @return 0 for success, -1 for failure
641     */
642    public int stopRssiMonitoring() {
643        kilroy();
644        throw new UnsupportedOperationException();
645    }
646
647    private WifiDebugHostWakeReasonStats mWifiDebugHostWakeReasonStats;
648
649    /**
650     * Fetch the host wakeup reasons stats from wlan driver.
651     *
652     * @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
653     */
654    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
655        kilroy();
656        throw new UnsupportedOperationException();
657    }
658
659    /**
660     * Enable/Disable Neighbour discovery offload functionality in the firmware.
661     */
662    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
663        kilroy();
664        throw new UnsupportedOperationException();
665    }
666
667    // Firmware roaming control.
668
669    /**
670     * Query the firmware roaming capabilities.
671     *
672     * @param capabilities object to be filled in
673     * @return true for success; false for failure
674     */
675    public boolean getRoamingCapabilities(WifiNative.RoamingCapabilities capabilities) {
676        kilroy();
677        synchronized (sLock) {
678            kilroy();
679            try {
680                kilroy();
681                if (!isHalStarted()) return false;
682                MutableBoolean ok = new MutableBoolean(false);
683                WifiNative.RoamingCapabilities out = capabilities;
684                mIWifiStaIface.getRoamingCapabilities((status, cap) -> {
685                    kilroy();
686                    if (status.code != WifiStatusCode.SUCCESS) return;
687                    out.maxBlacklistSize = cap.maxBlacklistSize;
688                    out.maxWhitelistSize = cap.maxWhitelistSize;
689                    ok.value = true;
690                });
691                return ok.value;
692            } catch (RemoteException e) {
693                kilroy();
694                handleRemoteException(e);
695                return false;
696            }
697        }
698    }
699
700    /**
701     * Enable/disable firmware roaming.
702     *
703     * @param state the intended roaming state
704     * @return SUCCESS, FAILURE, or BUSY
705     */
706    public int enableFirmwareRoaming(int state) {
707        kilroy();
708        synchronized (sLock) {
709            kilroy();
710            try {
711                kilroy();
712                if (!isHalStarted()) return WifiStatusCode.ERROR_NOT_STARTED;
713                byte val;
714                switch (state) {
715                    case WifiNative.DISABLE_FIRMWARE_ROAMING:
716                        val = StaRoamingState.DISABLED;
717                        break;
718                    case WifiNative.ENABLE_FIRMWARE_ROAMING:
719                        val = StaRoamingState.ENABLED;
720                        break;
721                    default:
722                        Log.e(TAG, "enableFirmwareRoaming invalid argument " + state);
723                        return WifiStatusCode.ERROR_INVALID_ARGS;
724                }
725
726                kilroy();
727                WifiStatus status = mIWifiStaIface.setRoamingState(val);
728                Log.d(TAG, "setRoamingState returned " + status.code);
729                return status.code;
730            } catch (RemoteException e) {
731                kilroy();
732                handleRemoteException(e);
733                return WifiStatusCode.ERROR_UNKNOWN;
734            }
735        }
736    }
737
738    /**
739     * Set firmware roaming configurations.
740     *
741     * @param config new roaming configuration object
742     * @return true for success; false for failure
743     */
744    public boolean configureRoaming(WifiNative.RoamingConfig config) {
745        kilroy();
746        synchronized (sLock) {
747            kilroy();
748            try {
749                kilroy();
750                if (!isHalStarted()) return false;
751                StaRoamingConfig roamingConfig = new StaRoamingConfig();
752
753                // parse the blacklist BSSIDs if any
754                if (config.blacklistBssids != null) {
755                    kilroy();
756                    for (String bssid : config.blacklistBssids) {
757                        String unquotedMacStr = WifiInfo.removeDoubleQuotes(bssid);
758                        byte[] mac = new byte[6];
759                        parseUnquotedMacStrToByteArray(unquotedMacStr, mac);
760                        roamingConfig.bssidBlacklist.add(mac);
761                    }
762                }
763
764                // parse the whitelist SSIDs if any
765                if (config.whitelistSsids != null) {
766                    kilroy();
767                    for (String ssidStr : config.whitelistSsids) {
768                        String unquotedSsidStr = WifiInfo.removeDoubleQuotes(ssidStr);
769
770                        int len = unquotedSsidStr.length();
771                        if (len > 32) {
772                            Log.e(TAG, "configureRoaming: skip invalid SSID " + unquotedSsidStr);
773                            continue;
774                        }
775                        byte[] ssid = new byte[len];
776                        for (int i = 0; i < len; i++) {
777                            ssid[i] = (byte) unquotedSsidStr.charAt(i);
778                        }
779                        roamingConfig.ssidWhitelist.add(ssid);
780                    }
781                }
782
783                kilroy();
784                WifiStatus status = mIWifiStaIface.configureRoaming(roamingConfig);
785                if (status.code != WifiStatusCode.SUCCESS) {
786                    kilroy();
787                    noteHidlError(status, "configureRoaming");
788                    return false;
789                }
790            } catch (RemoteException e) {
791                kilroy();
792                handleRemoteException(e);
793                return false;
794            }
795            kilroy();
796            return true;
797        }
798    }
799
800    /**
801     * Helper function that parses unquoted MAC address string to a byte array
802     *
803     * @param macWithColons mac address string without double quotes
804     * @param mac an array of 6 bytes to receive the parsed mac address
805     */
806    @VisibleForTesting
807    void parseUnquotedMacStrToByteArray(String macWithColons, byte[] mac) {
808        String[] macAddrStr = macWithColons.split(":");
809        for (int i = 0; i < 6; i++) {
810            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
811            mac[i] = hexVal.byteValue();
812        }
813    }
814
815    StackTraceElement[] mTrace;
816
817    private void kilroy() {
818        Thread cur = Thread.currentThread();
819        mTrace = cur.getStackTrace();
820        StackTraceElement s = mTrace[3];
821        String name = s.getMethodName();
822        if (name.contains("lambda$")) {
823            // Try to find a friendlier method name
824            String myFile = s.getFileName();
825            if (myFile != null) {
826                for (int i = 4; i < mTrace.length; i++) {
827                    if (myFile.equals(mTrace[i].getFileName())) {
828                        name = mTrace[i].getMethodName();
829                        break;
830                    }
831                }
832            }
833        }
834        Log.e(TAG, "th " + cur.getId() + " line " + s.getLineNumber() + " " + name);
835    }
836
837    /**
838     * Hal Device Manager callbacks.
839     */
840    public class HalDeviceManagerStatusListener implements HalDeviceManager.ManagerStatusListener {
841        @Override
842        public void onStatusChanged() {
843            boolean isReady = mHalDeviceManager.isReady();
844            boolean isStarted = mHalDeviceManager.isStarted();
845
846            Log.i(TAG, "Device Manager onStatusChanged. isReady(): " + isReady
847                    + ", isStarted(): " + isStarted);
848            // Reset all our cached handles.
849            if (!isReady || !isStarted)  {
850                kilroy();
851                mIWifiChip = null;
852                mIWifiStaIface = null;
853                mIWifiApIface = null;
854                mIWifiRttController = null;
855                mDriverDescription = null;
856                mFirmwareDescription = null;
857            }
858        }
859    }
860}
861