WifiVendorHal.java revision cbe44718452e93ef2b68974230231ff4fac99dee
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.IfaceType;
25import android.hardware.wifi.V1_0.StaRoamingConfig;
26import android.hardware.wifi.V1_0.StaRoamingState;
27import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
28import android.hardware.wifi.V1_0.WifiDebugPacketFateFrameType;
29import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
30import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
31import android.hardware.wifi.V1_0.WifiDebugRxPacketFate;
32import android.hardware.wifi.V1_0.WifiDebugRxPacketFateReport;
33import android.hardware.wifi.V1_0.WifiDebugTxPacketFate;
34import android.hardware.wifi.V1_0.WifiDebugTxPacketFateReport;
35import android.hardware.wifi.V1_0.WifiStatus;
36import android.hardware.wifi.V1_0.WifiStatusCode;
37import android.net.apf.ApfCapabilities;
38import android.net.wifi.RttManager;
39import android.net.wifi.RttManager.ResponderConfig;
40import android.net.wifi.WifiInfo;
41import android.net.wifi.WifiLinkLayerStats;
42import android.net.wifi.WifiManager;
43import android.net.wifi.WifiScanner;
44import android.net.wifi.WifiWakeReasonAndCounts;
45import android.os.HandlerThread;
46import android.os.RemoteException;
47import android.util.Log;
48import android.util.MutableBoolean;
49import android.util.MutableInt;
50
51import com.android.internal.annotations.VisibleForTesting;
52import com.android.internal.util.ArrayUtils;
53import com.android.server.connectivity.KeepalivePacketData;
54import com.android.server.wifi.util.BitMask;
55import com.android.server.wifi.util.NativeUtil;
56
57import java.util.ArrayList;
58import java.util.Set;
59
60/**
61 * Vendor HAL via HIDL
62 */
63public class WifiVendorHal {
64
65    private static final String TAG = "WifiVendorHal";
66
67    // Vendor HAL HIDL interface objects.
68    private IWifiChip mIWifiChip;
69    private IWifiStaIface mIWifiStaIface;
70    private IWifiApIface mIWifiApIface;
71    private IWifiRttController mIWifiRttController;
72    private final HalDeviceManager mHalDeviceManager;
73    private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
74    private final HandlerThread mWifiStateMachineHandlerThread;
75
76    public WifiVendorHal(HalDeviceManager halDeviceManager,
77                         HandlerThread wifiStateMachineHandlerThread) {
78        mHalDeviceManager = halDeviceManager;
79        mWifiStateMachineHandlerThread = wifiStateMachineHandlerThread;
80        mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener();
81    }
82
83    // TODO(mplass): figure out where we need locking in hidl world. b/33383725
84    public static final Object sLock = new Object();
85
86    private void handleRemoteException(RemoteException e) {
87        kilroy();
88        Log.e(TAG, "RemoteException in HIDL call " + e);
89    }
90
91    private void noteHidlError(WifiStatus status, String culprit) {
92        kilroy();
93        Log.e(TAG, "Error in " + culprit + " code: " + status.code
94                + " (" + status.description + ")");
95    }
96
97    /**
98     * Initialize the Hal device manager and register for status callbacks.
99     * @return
100     */
101    public boolean initialize() {
102        mHalDeviceManager.initialize();
103        mHalDeviceManager.registerStatusListener(
104                mHalDeviceManagerStatusCallbacks, mWifiStateMachineHandlerThread.getLooper());
105        return true;
106    }
107
108    /**
109     * Bring up the HIDL Vendor HAL and configure for AP (Access Point) mode
110     * @return true for success
111     */
112    public boolean startVendorHalAp() {
113        return startVendorHal(AP_MODE);
114    }
115
116     /**
117     * Bring up the HIDL Vendor HAL and configure for STA (Station) mode
118     * @return true for success
119     */
120    public boolean startVendorHalSta() {
121        return startVendorHal(STA_MODE);
122    }
123
124
125    public static final boolean STA_MODE = true;
126    public static final boolean AP_MODE = false;
127
128    /**
129     * Bring up the HIDL Vendor HAL and configure for STA mode or AP mode.
130     *
131     * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
132     */
133    public boolean startVendorHal(boolean isStaMode) {
134        if (!mHalDeviceManager.start()) {
135            Log.e(TAG, "Failed to start the vendor HAL");
136            return false;
137        }
138        IWifiIface iface;
139        if (isStaMode) {
140            mIWifiStaIface = mHalDeviceManager.createStaIface(null, null);
141            if (mIWifiStaIface == null) {
142                Log.e(TAG, "Failed to create STA Iface. Vendor Hal start failed");
143                mHalDeviceManager.stop();
144                return false;
145            }
146            iface = (IWifiIface) mIWifiStaIface;
147            mIWifiRttController = mHalDeviceManager.createRttController(iface);
148            if (mIWifiRttController == null) {
149                Log.e(TAG, "Failed to create RTT controller. Vendor Hal start failed");
150                stopVendorHal();
151                return false;
152            }
153            enableLinkLayerStats();
154        } else {
155            mIWifiApIface = mHalDeviceManager.createApIface(null, null);
156            if (mIWifiApIface == null) {
157                Log.e(TAG, "Failed to create AP Iface. Vendor Hal start failed");
158                stopVendorHal();
159                return false;
160            }
161            iface = (IWifiIface) mIWifiApIface;
162        }
163        mIWifiChip = mHalDeviceManager.getChip(iface);
164        if (mIWifiChip == null) {
165            Log.e(TAG, "Failed to get the chip created for the Iface. Vendor Hal start failed");
166            stopVendorHal();
167            return false;
168        }
169        Log.i(TAG, "Vendor Hal started successfully");
170        return true;
171    }
172
173    /**
174     * Stops the HAL
175     */
176    public void stopVendorHal() {
177        mHalDeviceManager.stop();
178        Log.i(TAG, "Vendor Hal stopped");
179    }
180
181    /**
182     * Tests whether the HAL is running or not
183     */
184    public boolean isHalStarted() {
185        return (mIWifiStaIface != null || mIWifiApIface != null);
186    }
187
188    /**
189     * Gets the scan capabilities
190     *
191     * @param capabilities object to be filled in
192     * @return true for success. false for failure
193     */
194    public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) {
195        kilroy();
196        throw new UnsupportedOperationException();
197    }
198
199    /**
200     * to be implemented
201     */
202    public boolean startScan(WifiNative.ScanSettings settings,
203                             WifiNative.ScanEventHandler eventHandler) {
204        kilroy();
205        throw new UnsupportedOperationException();
206    }
207
208    /**
209     * to be implemented
210     */
211    public void stopScan() {
212        kilroy();
213        throw new UnsupportedOperationException();
214    }
215
216    /**
217     * to be implemented
218     */
219    public void pauseScan() {
220        kilroy();
221        throw new UnsupportedOperationException();
222    }
223
224    /**
225     * to be implemented
226     */
227    public void restartScan() {
228        kilroy();
229        throw new UnsupportedOperationException();
230    }
231
232    /**
233     * to be implemented
234     */
235    public WifiScanner.ScanData[] getScanResults(boolean flush) {
236        kilroy();
237        throw new UnsupportedOperationException();
238    }
239
240    /**
241     * Get the link layer statistics
242     *
243     * Note - we always enable link layer stats on a STA interface.
244     *
245     * @return the statistics, or null if unable to do so
246     */
247    public WifiLinkLayerStats getWifiLinkLayerStats() {
248        kilroy();
249        synchronized (sLock) {
250            try {
251                if (mIWifiStaIface == null) return null;
252                kilroy();
253                WifiLinkLayerStats out = new WifiLinkLayerStats();
254                MutableBoolean ok = new MutableBoolean(false);
255                kilroy();
256                mIWifiStaIface.getLinkLayerStats((status, stats) -> {
257                            kilroy();
258                            if (status.code != WifiStatusCode.SUCCESS) return;
259                            out.status = 0; // TODO
260                            out.SSID = null; // TODO
261                            out.BSSID = null; // TODO
262                            out.beacon_rx = stats.iface.beaconRx;
263                            out.rssi_mgmt = stats.iface.avgRssiMgmt;
264                        /* WME Best Effort Access Category */
265                            out.rxmpdu_be = stats.iface.wmeBePktStats.rxMpdu;
266                            out.txmpdu_be = stats.iface.wmeBePktStats.txMpdu;
267                            out.lostmpdu_be = stats.iface.wmeBePktStats.lostMpdu;
268                            out.retries_be = stats.iface.wmeBePktStats.retries;
269                        /* WME Background Access Category */
270                            out.rxmpdu_bk = stats.iface.wmeBkPktStats.rxMpdu;
271                            out.txmpdu_bk = stats.iface.wmeBkPktStats.txMpdu;
272                            out.lostmpdu_bk = stats.iface.wmeBkPktStats.lostMpdu;
273                            out.retries_bk = stats.iface.wmeBkPktStats.retries;
274                        /* WME Video Access Category */
275                            out.rxmpdu_vi = stats.iface.wmeViPktStats.rxMpdu;
276                            out.txmpdu_vi = stats.iface.wmeViPktStats.txMpdu;
277                            out.lostmpdu_vi = stats.iface.wmeViPktStats.lostMpdu;
278                            out.retries_vi = stats.iface.wmeViPktStats.retries;
279                        /* WME Voice Access Category */
280                            out.rxmpdu_vo = stats.iface.wmeVoPktStats.rxMpdu;
281                            out.txmpdu_vo = stats.iface.wmeVoPktStats.txMpdu;
282                            out.lostmpdu_vo = stats.iface.wmeVoPktStats.lostMpdu;
283                            out.retries_vo = stats.iface.wmeVoPktStats.retries;
284                            out.on_time = stats.radio.onTimeInMs;
285                            out.tx_time = stats.radio.txTimeInMs;
286                            out.tx_time_per_level = new int[stats.radio.txTimeInMsPerLevel.size()];
287                            for (int i = 0; i < out.tx_time_per_level.length; i++) {
288                                out.tx_time_per_level[i] = stats.radio.txTimeInMsPerLevel.get(i);
289                            }
290                            out.rx_time = stats.radio.rxTimeInMs;
291                            out.on_time_scan = stats.radio.onTimeInMsForScan;
292                            kilroy();
293                            ok.value = true;
294                        }
295                );
296                return ok.value ? out : null;
297            } catch (RemoteException e) {
298                kilroy();
299                handleRemoteException(e);
300                return null;
301            }
302        }
303    }
304
305    @VisibleForTesting
306    boolean mLinkLayerStatsDebug = false;  // Passed to Hal
307
308    /**
309     * Enables the linkLayerStats in the Hal.
310     *
311     * This is called unconditionally whenever we create a STA interface.
312     *
313     */
314    private void enableLinkLayerStats() {
315        synchronized (sLock) {
316            try {
317                kilroy();
318                WifiStatus status;
319                status = mIWifiStaIface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
320                if (status.code != WifiStatusCode.SUCCESS) {
321                    kilroy();
322                    Log.e(TAG, "unable to enable link layer stats collection");
323                }
324            } catch (RemoteException e) {
325                kilroy();
326                handleRemoteException(e);
327            }
328        }
329    }
330
331    /**
332     * Translation table used by getSupportedFeatureSet for translating IWifiStaIface caps
333     */
334    private static final int[][] sFeatureCapabilityTranslation = {
335            {WifiManager.WIFI_FEATURE_INFRA_5G,
336                    IWifiStaIface.StaIfaceCapabilityMask.STA_5G
337            },
338            {WifiManager.WIFI_FEATURE_PASSPOINT,
339                    IWifiStaIface.StaIfaceCapabilityMask.HOTSPOT
340            },
341            {WifiManager.WIFI_FEATURE_SCANNER,
342                    IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN,
343            },
344            {WifiManager.WIFI_FEATURE_PNO,
345                    IWifiStaIface.StaIfaceCapabilityMask.PNO
346            },
347            {WifiManager.WIFI_FEATURE_TDLS,
348                    IWifiStaIface.StaIfaceCapabilityMask.TDLS
349            },
350            {WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL,
351                    IWifiStaIface.StaIfaceCapabilityMask.TDLS_OFFCHANNEL
352            },
353            {WifiManager.WIFI_FEATURE_LINK_LAYER_STATS,
354                    IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
355            },
356            {WifiManager.WIFI_FEATURE_RSSI_MONITOR,
357                    IWifiStaIface.StaIfaceCapabilityMask.RSSI_MONITOR
358            },
359            {WifiManager.WIFI_FEATURE_MKEEP_ALIVE,
360                    IWifiStaIface.StaIfaceCapabilityMask.KEEP_ALIVE
361            },
362            {WifiManager.WIFI_FEATURE_CONFIG_NDO,
363                    IWifiStaIface.StaIfaceCapabilityMask.ND_OFFLOAD
364            },
365            {WifiManager.WIFI_FEATURE_CONTROL_ROAMING,
366                    IWifiStaIface.StaIfaceCapabilityMask.CONTROL_ROAMING
367            },
368            {WifiManager.WIFI_FEATURE_IE_WHITELIST,
369                    IWifiStaIface.StaIfaceCapabilityMask.PROBE_IE_WHITELIST
370            },
371            {WifiManager.WIFI_FEATURE_SCAN_RAND,
372                    IWifiStaIface.StaIfaceCapabilityMask.SCAN_RAND
373            },
374    };
375
376    /**
377     * Feature bit mask translation for STAs
378     *
379     * @param capabilities bitmask defined IWifiStaIface.StaIfaceCapabilityMask
380     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
381     */
382    @VisibleForTesting
383    int wifiFeatureMaskFromStaCapabilities(int capabilities) {
384        int features = 0;
385        for (int i = 0; i < sFeatureCapabilityTranslation.length; i++) {
386            if ((capabilities & sFeatureCapabilityTranslation[i][1]) != 0) {
387                features |= sFeatureCapabilityTranslation[i][0];
388            }
389        }
390        return features;
391    }
392
393    /**
394     * Get the supported features
395     * <p>
396     * Note that not all the WifiManager.WIFI_FEATURE_* bits are supplied through
397     * this call. //TODO(b/34900537) fix this
398     *
399     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
400     */
401    public int getSupportedFeatureSet() {
402        int featureSet = 0;
403        try {
404            final MutableInt feat = new MutableInt(0);
405            synchronized (sLock) {
406                if (mIWifiStaIface != null) {
407                    mIWifiStaIface.getCapabilities((status, capabilities) -> {
408                        if (status.code != WifiStatusCode.SUCCESS) return;
409                        feat.value = wifiFeatureMaskFromStaCapabilities(capabilities);
410                    });
411                }
412            }
413            featureSet = feat.value;
414        } catch (RemoteException e) {
415            handleRemoteException(e);
416            return 0;
417        }
418
419        Set<Integer> supportedIfaceTypes = mHalDeviceManager.getSupportedIfaceTypes();
420        if (supportedIfaceTypes.contains(IfaceType.STA)) {
421            featureSet |= WifiManager.WIFI_FEATURE_INFRA;
422        }
423        if (supportedIfaceTypes.contains(IfaceType.AP)) {
424            featureSet |= WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT;
425        }
426        if (supportedIfaceTypes.contains(IfaceType.P2P)) {
427            featureSet |= WifiManager.WIFI_FEATURE_P2P;
428        }
429        if (supportedIfaceTypes.contains(IfaceType.NAN)) {
430            featureSet |= WifiManager.WIFI_FEATURE_AWARE;
431        }
432
433        return featureSet;
434    }
435
436    /* RTT related commands/events */
437
438    /**
439     * Starts a new rtt request
440     *
441     * @param params
442     * @param handler
443     * @return success indication
444     */
445    public boolean requestRtt(RttManager.RttParams[] params, WifiNative.RttEventHandler handler) {
446        kilroy();
447        throw new UnsupportedOperationException();
448    }
449
450    /**
451     * Cancels an outstanding rtt request
452     *
453     * @param params
454     * @return true if there was an outstanding request and it was successfully cancelled
455     */
456    public boolean cancelRtt(RttManager.RttParams[] params) {
457        kilroy();
458        throw new UnsupportedOperationException();
459    }
460
461    /**
462     * Enables RTT responder role on the device.
463     *
464     * @return {@link ResponderConfig} if the responder role is successfully enabled,
465     * {@code null} otherwise.
466     */
467    @Nullable
468    public ResponderConfig enableRttResponder(int timeoutSeconds) {
469        kilroy();
470        throw new UnsupportedOperationException();
471    }
472
473    /**
474     * Disables RTT responder role.
475     *
476     * @return {@code true} if responder role is successfully disabled,
477     * {@code false} otherwise.
478     */
479    public boolean disableRttResponder() {
480        kilroy();
481        throw new UnsupportedOperationException();
482    }
483
484    /**
485     * Set the MAC OUI during scanning.
486     *
487     * An OUI {Organizationally Unique Identifier} is a 24-bit number that
488     * uniquely identifies a vendor or manufacturer.
489     *
490     * @param oui
491     * @return true for success
492     */
493    public boolean setScanningMacOui(byte[] oui) {
494        kilroy();
495        if (oui == null) return false;
496        kilroy();
497        if (oui.length != 3) return false;
498        kilroy();
499        synchronized (sLock) {
500            try {
501                if (mIWifiStaIface == null) return false;
502                WifiStatus status = mIWifiStaIface.setScanningMacOui(oui);
503                if (status.code != WifiStatusCode.SUCCESS) return false;
504                kilroy();
505                return true;
506            } catch (RemoteException e) {
507                handleRemoteException(e);
508                return false;
509            }
510        }
511    }
512
513    /**
514     * not supported
515     */
516    public int[] getChannelsForBand(int band) {
517        kilroy();
518        throw new UnsupportedOperationException();
519    }
520
521    /**
522     * not supported
523     */
524    public boolean isGetChannelsForBandSupported() {
525        kilroy();
526        throw new UnsupportedOperationException();
527    }
528
529    /**
530     * Set DFS - actually, this is always on.
531     *
532     * @param dfsOn
533     * @return success indication
534     */
535    public boolean setDfsFlag(boolean dfsOn) {
536        kilroy();
537        throw new UnsupportedOperationException();
538    }
539
540    /**
541     * RTT (Round Trip Time) measurement capabilities of the device.
542     */
543    public RttManager.RttCapabilities getRttCapabilities() {
544        kilroy();
545        throw new UnsupportedOperationException();
546    }
547
548    /**
549     * Get the APF (Android Packet Filter) capabilities of the device
550     */
551    public ApfCapabilities getApfCapabilities() {
552        class AnswerBox {
553            public ApfCapabilities value = sNoApfCapabilities;
554        }
555        synchronized (sLock) {
556            try {
557                if (mIWifiStaIface == null) return sNoApfCapabilities;
558                AnswerBox box = new AnswerBox();
559                mIWifiStaIface.getApfPacketFilterCapabilities((status, capabilities) -> {
560                    if (status.code != WifiStatusCode.SUCCESS) return;
561                    box.value = new ApfCapabilities(
562                        /* apfVersionSupported */   capabilities.version,
563                        /* maximumApfProgramSize */ capabilities.maxLength,
564                        /* apfPacketFormat */       android.system.OsConstants.ARPHRD_ETHER);
565                });
566                return box.value;
567            } catch (RemoteException e) {
568                handleRemoteException(e);
569                return sNoApfCapabilities;
570            }
571        }
572    }
573
574    private static final ApfCapabilities sNoApfCapabilities = new ApfCapabilities(0, 0, 0);
575
576    /**
577     * Installs an APF program on this iface, replacing any existing program.
578     *
579     * @param filter is the android packet filter program
580     * @return true for success
581     */
582    public boolean installPacketFilter(byte[] filter) {
583        kilroy();
584        int cmdId = 0; //TODO(b/34901818) We only aspire to support one program at a time
585        if (filter == null) return false;
586        // Copy the program before taking the lock.
587        ArrayList<Byte> program = new ArrayList<>(filter.length);
588        for (byte b : filter) {
589            program.add(b);
590        }
591        synchronized (sLock) {
592            try {
593                if (mIWifiStaIface == null) return false;
594                WifiStatus status = mIWifiStaIface.installApfPacketFilter(cmdId, program);
595                if (status.code != WifiStatusCode.SUCCESS) return false;
596                kilroy();
597                return true;
598            } catch (RemoteException e) {
599                handleRemoteException(e);
600                return false;
601            }
602        }
603    }
604
605    /**
606     * Set country code for this AP iface.
607     *
608     * @param countryCode - two-letter country code (as ISO 3166)
609     * @return true for success
610     */
611    public boolean setCountryCodeHal(String countryCode) {
612        kilroy();
613        if (countryCode == null) return false;
614        if (countryCode.length() != 2) return false;
615        byte[] code;
616        try {
617            code = NativeUtil.stringToByteArray(countryCode);
618        } catch (IllegalArgumentException e) {
619            kilroy();
620            return false;
621        }
622        synchronized (sLock) {
623            try {
624                if (mIWifiApIface == null) return false;
625                kilroy();
626                WifiStatus status = mIWifiApIface.setCountryCode(code);
627                if (status.code != WifiStatusCode.SUCCESS) return false;
628                kilroy();
629                return true;
630            } catch (RemoteException e) {
631                handleRemoteException(e);
632                return false;
633            }
634        }
635    }
636
637    /**
638     * to be implemented TODO(b/34901821)
639     */
640    public boolean setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler) {
641        kilroy();
642        throw new UnsupportedOperationException();
643    }
644
645    /**
646     * Control debug data collection
647     *
648     * @param verboseLevel       0 to 3, inclusive. 0 stops logging.
649     * @param flags              Ignored.
650     * @param maxIntervalInSec   Maximum interval between reports; ignore if 0.
651     * @param minDataSizeInBytes Minimum data size in buffer for report; ignore if 0.
652     * @param ringName           Name of the ring for which data collection is to start.
653     * @return true for success
654     */
655    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
656                                          int minDataSizeInBytes, String ringName) {
657        kilroy();
658        synchronized (sLock) {
659            try {
660                if (mIWifiChip == null) return false;
661                kilroy();
662                // note - flags are not used
663                WifiStatus status = mIWifiChip.startLoggingToDebugRingBuffer(
664                        ringName,
665                        verboseLevel,
666                        maxIntervalInSec,
667                        minDataSizeInBytes
668                );
669                return status.code == WifiStatusCode.SUCCESS;
670            } catch (RemoteException e) {
671                kilroy();
672                handleRemoteException(e);
673                return false;
674            }
675        }
676    }
677
678    /**
679     * Pointlessly fail
680     *
681     * @return -1
682     */
683    public int getSupportedLoggerFeatureSet() {
684        return -1;
685    }
686
687    /**
688     * to be implemented TODO(b/34901821)
689     */
690    public boolean resetLogHandler() {
691        kilroy();
692        throw new UnsupportedOperationException();
693    }
694
695    private String mDriverDescription; // Cached value filled by requestChipDebugInfo()
696
697    /**
698     * Vendor-provided wifi driver version string
699     */
700    public String getDriverVersion() {
701        synchronized (sLock) {
702            if (mDriverDescription == null) requestChipDebugInfo();
703            return mDriverDescription;
704        }
705    }
706
707    private String mFirmwareDescription; // Cached value filled by requestChipDebugInfo()
708
709    /**
710     * Vendor-provided wifi firmware version string
711     */
712    public String getFirmwareVersion() {
713        synchronized (sLock) {
714            if (mFirmwareDescription == null) requestChipDebugInfo();
715            return mFirmwareDescription;
716        }
717    }
718
719    /**
720     * Refreshes our idea of the driver and firmware versions
721     */
722    private void requestChipDebugInfo() {
723        mDriverDescription = null;
724        mFirmwareDescription = null;
725        try {
726            if (mIWifiChip == null) return;
727            mIWifiChip.requestChipDebugInfo((status, chipDebugInfo) -> {
728                if (status.code != WifiStatusCode.SUCCESS) return;
729                mDriverDescription = chipDebugInfo.driverDescription;
730                mFirmwareDescription = chipDebugInfo.firmwareDescription;
731            });
732        } catch (RemoteException e) {
733            handleRemoteException(e);
734            return;
735        }
736        Log.e(TAG, "Driver: " + mDriverDescription + " Firmware: " + mFirmwareDescription);
737    }
738
739    /**
740     * Creates RingBufferStatus from the Hal version
741     */
742    private static WifiNative.RingBufferStatus ringBufferStatus(WifiDebugRingBufferStatus h) {
743        WifiNative.RingBufferStatus ans = new WifiNative.RingBufferStatus();
744        ans.name = h.ringName;
745        ans.flag = frameworkRingBufferFlagsFromHal(h.flags);
746        ans.ringBufferId = h.ringId;
747        ans.ringBufferByteSize = h.sizeInBytes;
748        ans.verboseLevel = h.verboseLevel;
749        // Remaining fields are unavailable
750        //  writtenBytes;
751        //  readBytes;
752        //  writtenRecords;
753        return ans;
754    }
755
756    /**
757     * Translates a hal wifiDebugRingBufferFlag to the WifiNative version
758     */
759    private static int frameworkRingBufferFlagsFromHal(int wifiDebugRingBufferFlag) {
760        BitMask checkoff = new BitMask(wifiDebugRingBufferFlag);
761        int flags = 0;
762        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES)) {
763            flags |= WifiNative.RingBufferStatus.HAS_BINARY_ENTRIES;
764        }
765        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES)) {
766            flags |= WifiNative.RingBufferStatus.HAS_ASCII_ENTRIES;
767        }
768        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES)) {
769            flags |= WifiNative.RingBufferStatus.HAS_PER_PACKET_ENTRIES;
770        }
771        if (checkoff.value != 0) {
772            throw new IllegalArgumentException("Unknown WifiDebugRingBufferFlag " + checkoff.value);
773        }
774        return flags;
775    }
776
777    /**
778     * Creates array of RingBufferStatus from the Hal version
779     */
780    private static WifiNative.RingBufferStatus[] makeRingBufferStatusArray(
781            ArrayList<WifiDebugRingBufferStatus> ringBuffers) {
782        WifiNative.RingBufferStatus[] ans = new WifiNative.RingBufferStatus[ringBuffers.size()];
783        int i = 0;
784        for (WifiDebugRingBufferStatus b : ringBuffers) {
785            ans[i++] = ringBufferStatus(b);
786        }
787        return ans;
788    }
789
790    /**
791     * API to get the status of all ring buffers supported by driver
792     */
793    public WifiNative.RingBufferStatus[] getRingBufferStatus() {
794        kilroy();
795        class AnswerBox {
796            public WifiNative.RingBufferStatus[] value = null;
797        }
798        AnswerBox ans = new AnswerBox();
799        synchronized (sLock) {
800            if (mIWifiChip == null) return null;
801            try {
802                kilroy();
803                mIWifiChip.getDebugRingBuffersStatus((status, ringBuffers) -> {
804                    kilroy();
805                    if (status.code != WifiStatusCode.SUCCESS) return;
806                    ans.value = makeRingBufferStatusArray(ringBuffers);
807                });
808            } catch (RemoteException e) {
809                kilroy();
810                handleRemoteException(e);
811                return null;
812            }
813        }
814        return ans.value;
815    }
816
817    /**
818     * indicates to driver that all
819     * the data has to be uploaded urgently
820     */
821    public boolean getRingBufferData(String ringName) {
822        kilroy();
823        synchronized (sLock) {
824            try {
825                if (mIWifiChip == null) return false;
826                kilroy();
827                WifiStatus status = mIWifiChip.forceDumpToDebugRingBuffer(ringName);
828                return status.code == WifiStatusCode.SUCCESS;
829            } catch (RemoteException e) {
830                handleRemoteException(e);
831                return false;
832            }
833        }
834    }
835
836    /**
837     * to be implemented via mIWifiChip.requestFirmwareDebugDump
838     */
839    public byte[] getFwMemoryDump() {
840        kilroy();
841        throw new UnsupportedOperationException();
842    }
843
844    /**
845     * Request vendor debug info from the driver
846     */
847    public byte[] getDriverStateDump() {
848        kilroy();
849        throw new UnsupportedOperationException();
850    }
851
852    /**
853     * Start packet fate monitoring
854     *
855     * Once started, monitoring remains active until HAL is unloaded.
856     *
857     * @return true for success
858     */
859    public boolean startPktFateMonitoring() {
860        kilroy();
861        synchronized (sLock) {
862            if (mIWifiStaIface == null) return false;
863            try {
864                kilroy();
865                WifiStatus status = mIWifiStaIface.startDebugPacketFateMonitoring();
866                return status.code == WifiStatusCode.SUCCESS;
867            } catch (RemoteException e) {
868                kilroy();
869                handleRemoteException(e);
870                return false;
871            }
872        }
873    }
874
875    private byte halToFrameworkPktFateFrameType(int type) {
876        switch (type) {
877            case WifiDebugPacketFateFrameType.UNKNOWN:
878                return WifiLoggerHal.FRAME_TYPE_UNKNOWN;
879            case WifiDebugPacketFateFrameType.ETHERNET_II:
880                return WifiLoggerHal.FRAME_TYPE_ETHERNET_II;
881            case WifiDebugPacketFateFrameType.MGMT_80211:
882                return WifiLoggerHal.FRAME_TYPE_80211_MGMT;
883            default:
884                throw new IllegalArgumentException("bad " + type);
885        }
886    }
887
888    private byte halToFrameworkRxPktFate(int type) {
889        switch (type) {
890            case WifiDebugRxPacketFate.SUCCESS:
891                return WifiLoggerHal.RX_PKT_FATE_SUCCESS;
892            case WifiDebugRxPacketFate.FW_QUEUED:
893                return WifiLoggerHal.RX_PKT_FATE_FW_QUEUED;
894            case WifiDebugRxPacketFate.FW_DROP_FILTER:
895                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER;
896            case WifiDebugRxPacketFate.FW_DROP_INVALID:
897                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID;
898            case WifiDebugRxPacketFate.FW_DROP_NOBUFS:
899                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS;
900            case WifiDebugRxPacketFate.FW_DROP_OTHER:
901                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER;
902            case WifiDebugRxPacketFate.DRV_QUEUED:
903                return WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED;
904            case WifiDebugRxPacketFate.DRV_DROP_FILTER:
905                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER;
906            case WifiDebugRxPacketFate.DRV_DROP_INVALID:
907                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID;
908            case WifiDebugRxPacketFate.DRV_DROP_NOBUFS:
909                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS;
910            case WifiDebugRxPacketFate.DRV_DROP_OTHER:
911                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER;
912            default:
913                throw new IllegalArgumentException("bad " + type);
914        }
915    }
916
917    private byte halToFrameworkTxPktFate(int type) {
918        switch (type) {
919            case WifiDebugTxPacketFate.ACKED:
920                return WifiLoggerHal.TX_PKT_FATE_ACKED;
921            case WifiDebugTxPacketFate.SENT:
922                return WifiLoggerHal.TX_PKT_FATE_SENT;
923            case WifiDebugTxPacketFate.FW_QUEUED:
924                return WifiLoggerHal.TX_PKT_FATE_FW_QUEUED;
925            case WifiDebugTxPacketFate.FW_DROP_INVALID:
926                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID;
927            case WifiDebugTxPacketFate.FW_DROP_NOBUFS:
928                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS;
929            case WifiDebugTxPacketFate.FW_DROP_OTHER:
930                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER;
931            case WifiDebugTxPacketFate.DRV_QUEUED:
932                return WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED;
933            case WifiDebugTxPacketFate.DRV_DROP_INVALID:
934                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID;
935            case WifiDebugTxPacketFate.DRV_DROP_NOBUFS:
936                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS;
937            case WifiDebugTxPacketFate.DRV_DROP_OTHER:
938                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER;
939            default:
940                throw new IllegalArgumentException("bad " + type);
941        }
942    }
943
944    /**
945     * Retrieve fates of outbound packets
946     *
947     * Reports the outbound frames for the most recent association (space allowing).
948     *
949     * @param reportBufs
950     * @return true for success
951     */
952    public boolean getTxPktFates(WifiNative.TxFateReport[] reportBufs) {
953        kilroy();
954        if (ArrayUtils.isEmpty(reportBufs)) return false;
955        synchronized (sLock) {
956            if (mIWifiStaIface == null) return false;
957            try {
958                kilroy();
959                MutableBoolean ok = new MutableBoolean(false);
960                mIWifiStaIface.getDebugTxPacketFates((status, fates) -> {
961                            kilroy();
962                            if (status.code != WifiStatusCode.SUCCESS) return;
963                            int i = 0;
964                            for (WifiDebugTxPacketFateReport fate : fates) {
965                                kilroy();
966                                if (i >= reportBufs.length) break;
967                                byte code = halToFrameworkTxPktFate(fate.fate);
968                                long us = fate.frameInfo.driverTimestampUsec;
969                                byte type =
970                                        halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
971                                byte[] frame =
972                                        NativeUtil.byteArrayFromArrayList(
973                                                fate.frameInfo.frameContent);
974                                reportBufs[i++] =
975                                        new WifiNative.TxFateReport(code, us, type, frame);
976                            }
977                            ok.value = true;
978                        }
979                );
980                return ok.value;
981            } catch (RemoteException e) {
982                kilroy();
983                handleRemoteException(e);
984                return false;
985            }
986        }
987    }
988
989    /**
990     * Retrieve fates of inbound packets
991     *
992     * Reports the inbound frames for the most recent association (space allowing).
993     *
994     * @param reportBufs
995     * @return true for success
996     */
997    public boolean getRxPktFates(WifiNative.RxFateReport[] reportBufs) {
998        kilroy();
999        if (ArrayUtils.isEmpty(reportBufs)) return false;
1000        synchronized (sLock) {
1001            if (mIWifiStaIface == null) return false;
1002            try {
1003                kilroy();
1004                MutableBoolean ok = new MutableBoolean(false);
1005                mIWifiStaIface.getDebugRxPacketFates((status, fates) -> {
1006                            kilroy();
1007                            if (status.code != WifiStatusCode.SUCCESS) return;
1008                            int i = 0;
1009                            for (WifiDebugRxPacketFateReport fate : fates) {
1010                                kilroy();
1011                                if (i >= reportBufs.length) break;
1012                                byte code = halToFrameworkRxPktFate(fate.fate);
1013                                long us = fate.frameInfo.driverTimestampUsec;
1014                                byte type =
1015                                        halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
1016                                byte[] frame =
1017                                        NativeUtil.byteArrayFromArrayList(
1018                                                fate.frameInfo.frameContent);
1019                                reportBufs[i++] =
1020                                        new WifiNative.RxFateReport(code, us, type, frame);
1021                            }
1022                            ok.value = true;
1023                        }
1024                );
1025                return ok.value;
1026            } catch (RemoteException e) {
1027                kilroy();
1028                handleRemoteException(e);
1029                return false;
1030            }
1031        }
1032    }
1033
1034    /**
1035     * Start sending the specified keep alive packets periodically.
1036     * @param slot
1037     * @param srcMac
1038     * @param keepAlivePacket
1039     * @param periodInMs
1040     * @return 0 for success, -1 for error
1041     */
1042    public int startSendingOffloadedPacket(
1043            int slot, byte[] srcMac, KeepalivePacketData keepAlivePacket, int periodInMs) {
1044        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " periodInMs=" + periodInMs);
1045
1046        ArrayList<Byte> data = NativeUtil.byteArrayToArrayList(keepAlivePacket.data);
1047        short protocol = (short) (keepAlivePacket.protocol);
1048
1049        synchronized (sLock) {
1050            if (mIWifiStaIface == null) return -1;
1051            try {
1052                WifiStatus status = mIWifiStaIface.startSendingKeepAlivePackets(
1053                        slot,
1054                        data,
1055                        protocol,
1056                        srcMac,
1057                        keepAlivePacket.dstMac,
1058                        periodInMs);
1059                if (status.code != WifiStatusCode.SUCCESS) return -1;
1060                return 0;
1061            } catch (RemoteException e) {
1062                kilroy();
1063                handleRemoteException(e);
1064                return -1;
1065            }
1066        }
1067    }
1068
1069    /**
1070     * Stop sending the specified keep alive packets.
1071     *
1072     * @param slot id - same as startSendingOffloadedPacket call.
1073     * @return 0 for success, -1 for error
1074     */
1075    public int stopSendingOffloadedPacket(int slot) {
1076        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
1077
1078        synchronized (sLock) {
1079            if (mIWifiStaIface == null) return -1;
1080            try {
1081                WifiStatus wifiStatus = mIWifiStaIface.stopSendingKeepAlivePackets(slot);
1082                if (wifiStatus.code != WifiStatusCode.SUCCESS) return -1;
1083                kilroy();
1084                return 0;
1085            } catch (RemoteException e) {
1086                handleRemoteException(e);
1087                return -1;
1088            }
1089        }
1090    }
1091
1092    /**
1093     * Start RSSI monitoring on the currently connected access point.
1094     *
1095     * @param maxRssi          Maximum RSSI threshold.
1096     * @param minRssi          Minimum RSSI threshold.
1097     * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
1098     * @return 0 for success, -1 for failure
1099     */
1100    public int startRssiMonitoring(byte maxRssi, byte minRssi,
1101                                   WifiNative.WifiRssiEventHandler rssiEventHandler) {
1102        kilroy();
1103        throw new UnsupportedOperationException();
1104    }
1105
1106    /**
1107     * Stop RSSI monitoring
1108     *
1109     * @return 0 for success, -1 for failure
1110     */
1111    public int stopRssiMonitoring() {
1112        kilroy();
1113        throw new UnsupportedOperationException();
1114    }
1115
1116    //TODO - belongs in NativeUtil
1117    private static int[] intsFromArrayList(ArrayList<Integer> a) {
1118        if (a == null) return null;
1119        int[] b = new int[a.size()];
1120        int i = 0;
1121        for (Integer e : a) b[i++] = e;
1122        return b;
1123    }
1124
1125    /**
1126     * Translates from Hal version of wake reason stats to the framework version of same
1127     *
1128     * @param h - Hal version of wake reason stats
1129     * @return framework version of same
1130     */
1131    private static WifiWakeReasonAndCounts halToFrameworkWakeReasons(
1132            WifiDebugHostWakeReasonStats h) {
1133        if (h == null) return null;
1134        WifiWakeReasonAndCounts ans = new WifiWakeReasonAndCounts();
1135        ans.totalCmdEventWake = h.totalCmdEventWakeCnt;
1136        ans.totalDriverFwLocalWake = h.totalDriverFwLocalWakeCnt;
1137        ans.totalRxDataWake = h.totalRxPacketWakeCnt;
1138        ans.rxUnicast = h.rxPktWakeDetails.rxUnicastCnt;
1139        ans.rxMulticast = h.rxPktWakeDetails.rxMulticastCnt;
1140        ans.rxBroadcast = h.rxPktWakeDetails.rxBroadcastCnt;
1141        ans.icmp = h.rxIcmpPkWakeDetails.icmpPkt;
1142        ans.icmp6 = h.rxIcmpPkWakeDetails.icmp6Pkt;
1143        ans.icmp6Ra = h.rxIcmpPkWakeDetails.icmp6Ra;
1144        ans.icmp6Na = h.rxIcmpPkWakeDetails.icmp6Na;
1145        ans.icmp6Ns = h.rxIcmpPkWakeDetails.icmp6Ns;
1146        ans.ipv4RxMulticast = h.rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt;
1147        ans.ipv6Multicast = h.rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt;
1148        ans.otherRxMulticast = h.rxMulticastPkWakeDetails.otherRxMulticastAddrCnt;
1149        ans.cmdEventWakeCntArray = intsFromArrayList(h.cmdEventWakeCntPerType);
1150        ans.driverFWLocalWakeCntArray = intsFromArrayList(h.driverFwLocalWakeCntPerType);
1151        return ans;
1152    }
1153
1154    /**
1155     * Fetch the host wakeup reasons stats from wlan driver.
1156     *
1157     * @return the |WifiWakeReasonAndCounts| from the wlan driver, or null on failure.
1158     */
1159    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
1160        kilroy();
1161        class AnswerBox {
1162            public WifiDebugHostWakeReasonStats value = null;
1163        }
1164        AnswerBox ans = new AnswerBox();
1165        synchronized (sLock) {
1166            if (mIWifiChip == null) return null;
1167            try {
1168                kilroy();
1169                mIWifiChip.getDebugHostWakeReasonStats((status, stats) -> {
1170                    kilroy();
1171                    if (status.code == WifiStatusCode.SUCCESS) {
1172                        ans.value = stats;
1173                    }
1174                });
1175                kilroy();
1176                return halToFrameworkWakeReasons(ans.value);
1177            } catch (RemoteException e) {
1178                kilroy();
1179                handleRemoteException(e);
1180                return null;
1181            }
1182        }
1183    }
1184
1185    /**
1186     * Enable/Disable Neighbour discovery offload functionality in the firmware.
1187     *
1188     * @param enabled true to enable, false to disable.
1189     */
1190    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
1191        kilroy();
1192        synchronized (sLock) {
1193            if (mIWifiStaIface == null) return false;
1194            kilroy();
1195            try {
1196                kilroy();
1197                WifiStatus wifiStatus = mIWifiStaIface.enableNdOffload(enabled);
1198                if (wifiStatus.code != WifiStatusCode.SUCCESS) {
1199                    kilroy();
1200                    noteHidlError(wifiStatus, "configureNeighborDiscoveryOffload");
1201                    return false;
1202                }
1203            } catch (RemoteException e) {
1204                kilroy();
1205                handleRemoteException(e);
1206                return false;
1207            }
1208        }
1209        return true;
1210    }
1211
1212    // Firmware roaming control.
1213
1214    /**
1215     * Query the firmware roaming capabilities.
1216     *
1217     * @param capabilities object to be filled in
1218     * @return true for success; false for failure
1219     */
1220    public boolean getRoamingCapabilities(WifiNative.RoamingCapabilities capabilities) {
1221        kilroy();
1222        synchronized (sLock) {
1223            kilroy();
1224            try {
1225                kilroy();
1226                if (!isHalStarted()) return false;
1227                MutableBoolean ok = new MutableBoolean(false);
1228                WifiNative.RoamingCapabilities out = capabilities;
1229                mIWifiStaIface.getRoamingCapabilities((status, cap) -> {
1230                    kilroy();
1231                    if (status.code != WifiStatusCode.SUCCESS) return;
1232                    out.maxBlacklistSize = cap.maxBlacklistSize;
1233                    out.maxWhitelistSize = cap.maxWhitelistSize;
1234                    ok.value = true;
1235                });
1236                return ok.value;
1237            } catch (RemoteException e) {
1238                kilroy();
1239                handleRemoteException(e);
1240                return false;
1241            }
1242        }
1243    }
1244
1245    /**
1246     * Enable/disable firmware roaming.
1247     *
1248     * @param state the intended roaming state
1249     * @return SUCCESS, FAILURE, or BUSY
1250     */
1251    public int enableFirmwareRoaming(int state) {
1252        kilroy();
1253        synchronized (sLock) {
1254            kilroy();
1255            try {
1256                kilroy();
1257                if (!isHalStarted()) return WifiStatusCode.ERROR_NOT_STARTED;
1258                byte val;
1259                switch (state) {
1260                    case WifiNative.DISABLE_FIRMWARE_ROAMING:
1261                        val = StaRoamingState.DISABLED;
1262                        break;
1263                    case WifiNative.ENABLE_FIRMWARE_ROAMING:
1264                        val = StaRoamingState.ENABLED;
1265                        break;
1266                    default:
1267                        Log.e(TAG, "enableFirmwareRoaming invalid argument " + state);
1268                        return WifiStatusCode.ERROR_INVALID_ARGS;
1269                }
1270
1271                kilroy();
1272                WifiStatus status = mIWifiStaIface.setRoamingState(val);
1273                Log.d(TAG, "setRoamingState returned " + status.code);
1274                return status.code;
1275            } catch (RemoteException e) {
1276                kilroy();
1277                handleRemoteException(e);
1278                return WifiStatusCode.ERROR_UNKNOWN;
1279            }
1280        }
1281    }
1282
1283    /**
1284     * Set firmware roaming configurations.
1285     *
1286     * @param config new roaming configuration object
1287     * @return true for success; false for failure
1288     */
1289    public boolean configureRoaming(WifiNative.RoamingConfig config) {
1290        kilroy();
1291        synchronized (sLock) {
1292            kilroy();
1293            try {
1294                kilroy();
1295                if (!isHalStarted()) return false;
1296                StaRoamingConfig roamingConfig = new StaRoamingConfig();
1297
1298                // parse the blacklist BSSIDs if any
1299                if (config.blacklistBssids != null) {
1300                    kilroy();
1301                    for (String bssid : config.blacklistBssids) {
1302                        byte[] mac = NativeUtil.macAddressToByteArray(bssid);
1303                        roamingConfig.bssidBlacklist.add(mac);
1304                    }
1305                }
1306
1307                // parse the whitelist SSIDs if any
1308                if (config.whitelistSsids != null) {
1309                    kilroy();
1310                    for (String ssidStr : config.whitelistSsids) {
1311                        String unquotedSsidStr = WifiInfo.removeDoubleQuotes(ssidStr);
1312
1313                        int len = unquotedSsidStr.length();
1314                        if (len > 32) {
1315                            Log.e(TAG, "configureRoaming: skip invalid SSID " + unquotedSsidStr);
1316                            continue;
1317                        }
1318                        byte[] ssid = new byte[len];
1319                        for (int i = 0; i < len; i++) {
1320                            ssid[i] = (byte) unquotedSsidStr.charAt(i);
1321                        }
1322                        roamingConfig.ssidWhitelist.add(ssid);
1323                    }
1324                }
1325
1326                kilroy();
1327                WifiStatus status = mIWifiStaIface.configureRoaming(roamingConfig);
1328                if (status.code != WifiStatusCode.SUCCESS) {
1329                    kilroy();
1330                    noteHidlError(status, "configureRoaming");
1331                    return false;
1332                }
1333            } catch (RemoteException e) {
1334                kilroy();
1335                handleRemoteException(e);
1336                return false;
1337            }
1338            kilroy();
1339            return true;
1340        }
1341    }
1342
1343    StackTraceElement[] mTrace;
1344
1345    private void kilroy() {
1346        Thread cur = Thread.currentThread();
1347        mTrace = cur.getStackTrace();
1348        StackTraceElement s = mTrace[3];
1349        String name = s.getMethodName();
1350        if (name.contains("lambda$")) {
1351            // Try to find a friendlier method name
1352            String myFile = s.getFileName();
1353            if (myFile != null) {
1354                for (int i = 4; i < mTrace.length; i++) {
1355                    if (myFile.equals(mTrace[i].getFileName())) {
1356                        name = mTrace[i].getMethodName();
1357                        break;
1358                    }
1359                }
1360            }
1361        }
1362        Log.e(TAG, "th " + cur.getId() + " line " + s.getLineNumber() + " " + name);
1363    }
1364
1365    /**
1366     * Hal Device Manager callbacks.
1367     */
1368    public class HalDeviceManagerStatusListener implements HalDeviceManager.ManagerStatusListener {
1369        @Override
1370        public void onStatusChanged() {
1371            boolean isReady = mHalDeviceManager.isReady();
1372            boolean isStarted = mHalDeviceManager.isStarted();
1373
1374            Log.i(TAG, "Device Manager onStatusChanged. isReady(): " + isReady
1375                    + ", isStarted(): " + isStarted);
1376            // Reset all our cached handles.
1377            if (!isReady || !isStarted)  {
1378                kilroy();
1379                mIWifiChip = null;
1380                mIWifiStaIface = null;
1381                mIWifiApIface = null;
1382                mIWifiRttController = null;
1383                mDriverDescription = null;
1384                mFirmwareDescription = null;
1385            }
1386        }
1387    }
1388}
1389